1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2006 IronPort Systems Inc. <ambrisko@ironport.com> 5 * All rights reserved. 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 #ifdef LOCAL_MODULE 45 #include <ipmi.h> 46 #include <ipmivars.h> 47 #else 48 #include <sys/ipmi.h> 49 #include <dev/ipmi/ipmivars.h> 50 #endif 51 52 static void smic_wait_for_tx_okay(struct ipmi_softc *); 53 static void smic_wait_for_rx_okay(struct ipmi_softc *); 54 static void smic_wait_for_not_busy(struct ipmi_softc *); 55 static void smic_set_busy(struct ipmi_softc *); 56 57 static void 58 smic_wait_for_tx_okay(struct ipmi_softc *sc) 59 { 60 int flags; 61 62 do { 63 flags = INB(sc, SMIC_FLAGS); 64 } while (!(flags & SMIC_STATUS_TX_RDY)); 65 } 66 67 static void 68 smic_wait_for_rx_okay(struct ipmi_softc *sc) 69 { 70 int flags; 71 72 do { 73 flags = INB(sc, SMIC_FLAGS); 74 } while (!(flags & SMIC_STATUS_RX_RDY)); 75 } 76 77 static void 78 smic_wait_for_not_busy(struct ipmi_softc *sc) 79 { 80 int flags; 81 82 do { 83 flags = INB(sc, SMIC_FLAGS); 84 } while (flags & SMIC_STATUS_BUSY); 85 } 86 87 static void 88 smic_set_busy(struct ipmi_softc *sc) 89 { 90 int flags; 91 92 flags = INB(sc, SMIC_FLAGS); 93 flags |= SMIC_STATUS_BUSY; 94 flags &= ~SMIC_STATUS_RESERVED; 95 OUTB(sc, SMIC_FLAGS, flags); 96 } 97 98 /* 99 * Start a transfer with a WR_START transaction that sends the NetFn/LUN 100 * address. 101 */ 102 static int 103 smic_start_write(struct ipmi_softc *sc, u_char data) 104 { 105 u_char error, status; 106 107 smic_wait_for_not_busy(sc); 108 109 OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_WR_START); 110 OUTB(sc, SMIC_DATA, data); 111 smic_set_busy(sc); 112 smic_wait_for_not_busy(sc); 113 status = INB(sc, SMIC_CTL_STS); 114 if (status != SMIC_SC_SMS_WR_START) { 115 error = INB(sc, SMIC_DATA); 116 device_printf(sc->ipmi_dev, "SMIC: Write did not start %02x\n", 117 error); 118 return (0); 119 } 120 return (1); 121 } 122 123 /* 124 * Write a byte in the middle of the message (either the command or one of 125 * the data bytes) using a WR_NEXT transaction. 126 */ 127 static int 128 smic_write_next(struct ipmi_softc *sc, u_char data) 129 { 130 u_char error, status; 131 132 smic_wait_for_tx_okay(sc); 133 OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_WR_NEXT); 134 OUTB(sc, SMIC_DATA, data); 135 smic_set_busy(sc); 136 smic_wait_for_not_busy(sc); 137 status = INB(sc, SMIC_CTL_STS); 138 if (status != SMIC_SC_SMS_WR_NEXT) { 139 error = INB(sc, SMIC_DATA); 140 device_printf(sc->ipmi_dev, "SMIC: Write did not next %02x\n", 141 error); 142 return (0); 143 } 144 return (1); 145 } 146 147 /* 148 * Write the last byte of a transfer to end the write phase via a WR_END 149 * transaction. 150 */ 151 static int 152 smic_write_last(struct ipmi_softc *sc, u_char data) 153 { 154 u_char error, status; 155 156 smic_wait_for_tx_okay(sc); 157 OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_WR_END); 158 OUTB(sc, SMIC_DATA, data); 159 smic_set_busy(sc); 160 smic_wait_for_not_busy(sc); 161 status = INB(sc, SMIC_CTL_STS); 162 if (status != SMIC_SC_SMS_WR_END) { 163 error = INB(sc, SMIC_DATA); 164 device_printf(sc->ipmi_dev, "SMIC: Write did not end %02x\n", 165 error); 166 return (0); 167 } 168 return (1); 169 } 170 171 /* 172 * Start the read phase of a transfer with a RD_START transaction. 173 */ 174 static int 175 smic_start_read(struct ipmi_softc *sc, u_char *data) 176 { 177 u_char error, status; 178 179 smic_wait_for_not_busy(sc); 180 181 smic_wait_for_rx_okay(sc); 182 OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_RD_START); 183 smic_set_busy(sc); 184 smic_wait_for_not_busy(sc); 185 status = INB(sc, SMIC_CTL_STS); 186 if (status != SMIC_SC_SMS_RD_START) { 187 error = INB(sc, SMIC_DATA); 188 device_printf(sc->ipmi_dev, "SMIC: Read did not start %02x\n", 189 error); 190 return (0); 191 } 192 *data = INB(sc, SMIC_DATA); 193 return (1); 194 } 195 196 /* 197 * Read a byte via a RD_NEXT transaction. If this was the last byte, return 198 * 2 rather than 1. 199 */ 200 static int 201 smic_read_byte(struct ipmi_softc *sc, u_char *data) 202 { 203 u_char error, status; 204 205 smic_wait_for_rx_okay(sc); 206 OUTB(sc, SMIC_CTL_STS, SMIC_SC_SMS_RD_NEXT); 207 smic_set_busy(sc); 208 smic_wait_for_not_busy(sc); 209 status = INB(sc, SMIC_CTL_STS); 210 if (status != SMIC_SC_SMS_RD_NEXT && 211 status != SMIC_SC_SMS_RD_END) { 212 error = INB(sc, SMIC_DATA); 213 device_printf(sc->ipmi_dev, "SMIC: Read did not next %02x\n", 214 error); 215 return (0); 216 } 217 *data = INB(sc, SMIC_DATA); 218 if (status == SMIC_SC_SMS_RD_NEXT) 219 return (1); 220 else 221 return (2); 222 } 223 224 /* Complete a transfer via a RD_END transaction after reading the last byte. */ 225 static int 226 smic_read_end(struct ipmi_softc *sc) 227 { 228 u_char error, status; 229 230 OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_RD_END); 231 smic_set_busy(sc); 232 smic_wait_for_not_busy(sc); 233 status = INB(sc, SMIC_CTL_STS); 234 if (status != SMIC_SC_SMS_RDY) { 235 error = INB(sc, SMIC_DATA); 236 device_printf(sc->ipmi_dev, "SMIC: Read did not end %02x\n", 237 error); 238 return (0); 239 } 240 return (1); 241 } 242 243 static int 244 smic_polled_request(struct ipmi_softc *sc, struct ipmi_request *req) 245 { 246 u_char *cp, data; 247 int i, state; 248 249 /* First, start the message with the address. */ 250 if (!smic_start_write(sc, req->ir_addr)) 251 return (0); 252 #ifdef SMIC_DEBUG 253 device_printf(sc->ipmi_dev, "SMIC: WRITE_START address: %02x\n", 254 req->ir_addr); 255 #endif 256 257 if (req->ir_requestlen == 0) { 258 /* Send the command as the last byte. */ 259 if (!smic_write_last(sc, req->ir_command)) 260 return (0); 261 #ifdef SMIC_DEBUG 262 device_printf(sc->ipmi_dev, "SMIC: Wrote command: %02x\n", 263 req->ir_command); 264 #endif 265 } else { 266 /* Send the command. */ 267 if (!smic_write_next(sc, req->ir_command)) 268 return (0); 269 #ifdef SMIC_DEBUG 270 device_printf(sc->ipmi_dev, "SMIC: Wrote command: %02x\n", 271 req->ir_command); 272 #endif 273 274 /* Send the payload. */ 275 cp = req->ir_request; 276 for (i = 0; i < req->ir_requestlen - 1; i++) { 277 if (!smic_write_next(sc, *cp++)) 278 return (0); 279 #ifdef SMIC_DEBUG 280 device_printf(sc->ipmi_dev, "SMIC: Wrote data: %02x\n", 281 cp[-1]); 282 #endif 283 } 284 if (!smic_write_last(sc, *cp)) 285 return (0); 286 #ifdef SMIC_DEBUG 287 device_printf(sc->ipmi_dev, "SMIC: Write last data: %02x\n", 288 *cp); 289 #endif 290 } 291 292 /* Start the read phase by reading the NetFn/LUN. */ 293 if (smic_start_read(sc, &data) != 1) 294 return (0); 295 #ifdef SMIC_DEBUG 296 device_printf(sc->ipmi_dev, "SMIC: Read address: %02x\n", data); 297 #endif 298 if (data != IPMI_REPLY_ADDR(req->ir_addr)) { 299 device_printf(sc->ipmi_dev, "SMIC: Reply address mismatch\n"); 300 return (0); 301 } 302 303 /* Read the command. */ 304 if (smic_read_byte(sc, &data) != 1) 305 return (0); 306 #ifdef SMIC_DEBUG 307 device_printf(sc->ipmi_dev, "SMIC: Read command: %02x\n", data); 308 #endif 309 if (data != req->ir_command) { 310 device_printf(sc->ipmi_dev, "SMIC: Command mismatch\n"); 311 return (0); 312 } 313 314 /* Read the completion code. */ 315 state = smic_read_byte(sc, &req->ir_compcode); 316 if (state == 0) 317 return (0); 318 #ifdef SMIC_DEBUG 319 device_printf(sc->ipmi_dev, "SMIC: Read completion code: %02x\n", 320 req->ir_compcode); 321 #endif 322 323 /* Finally, read the reply from the BMC. */ 324 i = 0; 325 while (state == 1) { 326 state = smic_read_byte(sc, &data); 327 if (state == 0) 328 return (0); 329 if (i < req->ir_replybuflen) { 330 req->ir_reply[i] = data; 331 #ifdef SMIC_DEBUG 332 device_printf(sc->ipmi_dev, "SMIC: Read data: %02x\n", 333 data); 334 } else { 335 device_printf(sc->ipmi_dev, 336 "SMIC: Read short %02x byte %d\n", data, i + 1); 337 #endif 338 } 339 i++; 340 } 341 342 /* Terminate the transfer. */ 343 if (!smic_read_end(sc)) 344 return (0); 345 req->ir_replylen = i; 346 #ifdef SMIC_DEBUG 347 device_printf(sc->ipmi_dev, "SMIC: READ finished (%d bytes)\n", i); 348 if (req->ir_replybuflen < i) 349 #else 350 if (req->ir_replybuflen < i && req->ir_replybuflen != 0) 351 #endif 352 device_printf(sc->ipmi_dev, 353 "SMIC: Read short: %zd buffer, %d actual\n", 354 req->ir_replybuflen, i); 355 return (1); 356 } 357 358 static void 359 smic_loop(void *arg) 360 { 361 struct ipmi_softc *sc = arg; 362 struct ipmi_request *req; 363 int i, ok; 364 365 IPMI_LOCK(sc); 366 while ((req = ipmi_dequeue_request(sc)) != NULL) { 367 IPMI_UNLOCK(sc); 368 ok = 0; 369 for (i = 0; i < 3 && !ok; i++) { 370 IPMI_IO_LOCK(sc); 371 ok = smic_polled_request(sc, req); 372 IPMI_IO_UNLOCK(sc); 373 } 374 if (ok) 375 req->ir_error = 0; 376 else 377 req->ir_error = EIO; 378 IPMI_LOCK(sc); 379 ipmi_complete_request(sc, req); 380 } 381 IPMI_UNLOCK(sc); 382 kproc_exit(0); 383 } 384 385 static int 386 smic_startup(struct ipmi_softc *sc) 387 { 388 389 return (kproc_create(smic_loop, sc, &sc->ipmi_kthread, 0, 0, 390 "%s: smic", device_get_nameunit(sc->ipmi_dev))); 391 } 392 393 static int 394 smic_driver_request(struct ipmi_softc *sc, struct ipmi_request *req, int timo) 395 { 396 int i, ok; 397 398 ok = 0; 399 for (i = 0; i < 3 && !ok; i++) { 400 IPMI_IO_LOCK(sc); 401 ok = smic_polled_request(sc, req); 402 IPMI_IO_UNLOCK(sc); 403 } 404 if (ok) 405 req->ir_error = 0; 406 else 407 req->ir_error = EIO; 408 return (req->ir_error); 409 } 410 411 int 412 ipmi_smic_attach(struct ipmi_softc *sc) 413 { 414 int flags; 415 416 /* Setup function pointers. */ 417 sc->ipmi_startup = smic_startup; 418 sc->ipmi_enqueue_request = ipmi_polled_enqueue_request; 419 sc->ipmi_driver_request = smic_driver_request; 420 sc->ipmi_driver_requests_polled = 1; 421 422 /* See if we can talk to the controller. */ 423 flags = INB(sc, SMIC_FLAGS); 424 if (flags == 0xff) { 425 device_printf(sc->ipmi_dev, "couldn't find it\n"); 426 return (ENXIO); 427 } 428 429 #ifdef SMIC_DEBUG 430 device_printf(sc->ipmi_dev, "SMIC: initial state: %02x\n", flags); 431 #endif 432 433 return (0); 434 } 435