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