1 /*- 2 * Copyright (c) 2006 IronPort Systems Inc. <ambrisko@ironport.com> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD: head/sys/dev/ipmi/ipmi_smic.c 248705 2013-03-25 14:30:34Z melifaro $ 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/conf.h> 39 40 #ifdef LOCAL_MODULE 41 #include <ipmi.h> 42 #include <ipmivars.h> 43 #else 44 #include <sys/ipmi.h> 45 #include <dev/misc/ipmi/ipmivars.h> 46 #endif 47 48 static void smic_wait_for_tx_okay(struct ipmi_softc *); 49 static void smic_wait_for_rx_okay(struct ipmi_softc *); 50 static void smic_wait_for_not_busy(struct ipmi_softc *); 51 static void smic_set_busy(struct ipmi_softc *); 52 53 static void 54 smic_wait_for_tx_okay(struct ipmi_softc *sc) 55 { 56 int flags; 57 58 do { 59 flags = INB(sc, SMIC_FLAGS); 60 } while (!(flags & SMIC_STATUS_TX_RDY)); 61 } 62 63 static void 64 smic_wait_for_rx_okay(struct ipmi_softc *sc) 65 { 66 int flags; 67 68 do { 69 flags = INB(sc, SMIC_FLAGS); 70 } while (!(flags & SMIC_STATUS_RX_RDY)); 71 } 72 73 static void 74 smic_wait_for_not_busy(struct ipmi_softc *sc) 75 { 76 int flags; 77 78 do { 79 flags = INB(sc, SMIC_FLAGS); 80 } while (flags & SMIC_STATUS_BUSY); 81 } 82 83 static void 84 smic_set_busy(struct ipmi_softc *sc) 85 { 86 int flags; 87 88 flags = INB(sc, SMIC_FLAGS); 89 flags |= SMIC_STATUS_BUSY; 90 flags &= ~SMIC_STATUS_RESERVED; 91 OUTB(sc, SMIC_FLAGS, flags); 92 } 93 94 /* 95 * Start a transfer with a WR_START transaction that sends the NetFn/LUN 96 * address. 97 */ 98 static int 99 smic_start_write(struct ipmi_softc *sc, u_char data) 100 { 101 u_char error, status; 102 103 smic_wait_for_not_busy(sc); 104 105 OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_WR_START); 106 OUTB(sc, SMIC_DATA, data); 107 smic_set_busy(sc); 108 smic_wait_for_not_busy(sc); 109 status = INB(sc, SMIC_CTL_STS); 110 if (status != SMIC_SC_SMS_WR_START) { 111 error = INB(sc, SMIC_DATA); 112 device_printf(sc->ipmi_dev, "SMIC: Write did not start %02x\n", 113 error); 114 return (0); 115 } 116 return (1); 117 } 118 119 /* 120 * Write a byte in the middle of the message (either the command or one of 121 * the data bytes) using a WR_NEXT transaction. 122 */ 123 static int 124 smic_write_next(struct ipmi_softc *sc, u_char data) 125 { 126 u_char error, status; 127 128 smic_wait_for_tx_okay(sc); 129 OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_WR_NEXT); 130 OUTB(sc, SMIC_DATA, data); 131 smic_set_busy(sc); 132 smic_wait_for_not_busy(sc); 133 status = INB(sc, SMIC_CTL_STS); 134 if (status != SMIC_SC_SMS_WR_NEXT) { 135 error = INB(sc, SMIC_DATA); 136 device_printf(sc->ipmi_dev, "SMIC: Write did not next %02x\n", 137 error); 138 return (0); 139 } 140 return (1); 141 } 142 143 /* 144 * Write the last byte of a transfer to end the write phase via a WR_END 145 * transaction. 146 */ 147 static int 148 smic_write_last(struct ipmi_softc *sc, u_char data) 149 { 150 u_char error, status; 151 152 smic_wait_for_tx_okay(sc); 153 OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_WR_END); 154 OUTB(sc, SMIC_DATA, data); 155 smic_set_busy(sc); 156 smic_wait_for_not_busy(sc); 157 status = INB(sc, SMIC_CTL_STS); 158 if (status != SMIC_SC_SMS_WR_END) { 159 error = INB(sc, SMIC_DATA); 160 device_printf(sc->ipmi_dev, "SMIC: Write did not end %02x\n", 161 error); 162 return (0); 163 } 164 return (1); 165 } 166 167 /* 168 * Start the read phase of a transfer with a RD_START transaction. 169 */ 170 static int 171 smic_start_read(struct ipmi_softc *sc, u_char *data) 172 { 173 u_char error, status; 174 175 smic_wait_for_not_busy(sc); 176 177 smic_wait_for_rx_okay(sc); 178 OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_RD_START); 179 smic_set_busy(sc); 180 smic_wait_for_not_busy(sc); 181 status = INB(sc, SMIC_CTL_STS); 182 if (status != SMIC_SC_SMS_RD_START) { 183 error = INB(sc, SMIC_DATA); 184 device_printf(sc->ipmi_dev, "SMIC: Read did not start %02x\n", 185 error); 186 return (0); 187 } 188 *data = INB(sc, SMIC_DATA); 189 return (1); 190 } 191 192 /* 193 * Read a byte via a RD_NEXT transaction. If this was the last byte, return 194 * 2 rather than 1. 195 */ 196 static int 197 smic_read_byte(struct ipmi_softc *sc, u_char *data) 198 { 199 u_char error, status; 200 201 smic_wait_for_rx_okay(sc); 202 OUTB(sc, SMIC_CTL_STS, SMIC_SC_SMS_RD_NEXT); 203 smic_set_busy(sc); 204 smic_wait_for_not_busy(sc); 205 status = INB(sc, SMIC_CTL_STS); 206 if (status != SMIC_SC_SMS_RD_NEXT && 207 status != SMIC_SC_SMS_RD_END) { 208 error = INB(sc, SMIC_DATA); 209 device_printf(sc->ipmi_dev, "SMIC: Read did not next %02x\n", 210 error); 211 return (0); 212 } 213 *data = INB(sc, SMIC_DATA); 214 if (status == SMIC_SC_SMS_RD_NEXT) 215 return (1); 216 else 217 return (2); 218 } 219 220 /* Complete a transfer via a RD_END transaction after reading the last byte. */ 221 static int 222 smic_read_end(struct ipmi_softc *sc) 223 { 224 u_char error, status; 225 226 OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_RD_END); 227 smic_set_busy(sc); 228 smic_wait_for_not_busy(sc); 229 status = INB(sc, SMIC_CTL_STS); 230 if (status != SMIC_SC_SMS_RDY) { 231 error = INB(sc, SMIC_DATA); 232 device_printf(sc->ipmi_dev, "SMIC: Read did not end %02x\n", 233 error); 234 return (0); 235 } 236 return (1); 237 } 238 239 static int 240 smic_polled_request(struct ipmi_softc *sc, struct ipmi_request *req) 241 { 242 u_char *cp, data; 243 int i, state; 244 245 /* First, start the message with the address. */ 246 if (!smic_start_write(sc, req->ir_addr)) 247 return (0); 248 #ifdef SMIC_DEBUG 249 device_printf(sc->ipmi_dev, "SMIC: WRITE_START address: %02x\n", 250 req->ir_addr); 251 #endif 252 253 if (req->ir_requestlen == 0) { 254 /* Send the command as the last byte. */ 255 if (!smic_write_last(sc, req->ir_command)) 256 return (0); 257 #ifdef SMIC_DEBUG 258 device_printf(sc->ipmi_dev, "SMIC: Wrote command: %02x\n", 259 req->ir_command); 260 #endif 261 } else { 262 /* Send the command. */ 263 if (!smic_write_next(sc, req->ir_command)) 264 return (0); 265 #ifdef SMIC_DEBUG 266 device_printf(sc->ipmi_dev, "SMIC: Wrote command: %02x\n", 267 req->ir_command); 268 #endif 269 270 /* Send the payload. */ 271 cp = req->ir_request; 272 for (i = 0; i < req->ir_requestlen - 1; i++) { 273 if (!smic_write_next(sc, *cp++)) 274 return (0); 275 #ifdef SMIC_DEBUG 276 device_printf(sc->ipmi_dev, "SMIC: Wrote data: %02x\n", 277 cp[-1]); 278 #endif 279 } 280 if (!smic_write_last(sc, *cp)) 281 return (0); 282 #ifdef SMIC_DEBUG 283 device_printf(sc->ipmi_dev, "SMIC: Write last data: %02x\n", 284 *cp); 285 #endif 286 } 287 288 /* Start the read phase by reading the NetFn/LUN. */ 289 if (smic_start_read(sc, &data) != 1) 290 return (0); 291 #ifdef SMIC_DEBUG 292 device_printf(sc->ipmi_dev, "SMIC: Read address: %02x\n", data); 293 #endif 294 if (data != IPMI_REPLY_ADDR(req->ir_addr)) { 295 device_printf(sc->ipmi_dev, "SMIC: Reply address mismatch\n"); 296 return (0); 297 } 298 299 /* Read the command. */ 300 if (smic_read_byte(sc, &data) != 1) 301 return (0); 302 #ifdef SMIC_DEBUG 303 device_printf(sc->ipmi_dev, "SMIC: Read command: %02x\n", data); 304 #endif 305 if (data != req->ir_command) { 306 device_printf(sc->ipmi_dev, "SMIC: Command mismatch\n"); 307 return (0); 308 } 309 310 /* Read the completion code. */ 311 state = smic_read_byte(sc, &req->ir_compcode); 312 if (state == 0) 313 return (0); 314 #ifdef SMIC_DEBUG 315 device_printf(sc->ipmi_dev, "SMIC: Read completion code: %02x\n", 316 req->ir_compcode); 317 #endif 318 319 /* Finally, read the reply from the BMC. */ 320 i = 0; 321 while (state == 1) { 322 state = smic_read_byte(sc, &data); 323 if (state == 0) 324 return (0); 325 if (i < req->ir_replybuflen) { 326 req->ir_reply[i] = data; 327 #ifdef SMIC_DEBUG 328 device_printf(sc->ipmi_dev, "SMIC: Read data: %02x\n", 329 data); 330 } else { 331 device_printf(sc->ipmi_dev, 332 "SMIC: Read short %02x byte %d\n", data, i + 1); 333 #endif 334 } 335 i++; 336 } 337 338 /* Terminate the transfer. */ 339 if (!smic_read_end(sc)) 340 return (0); 341 req->ir_replylen = i; 342 #ifdef SMIC_DEBUG 343 device_printf(sc->ipmi_dev, "SMIC: READ finished (%d bytes)\n", i); 344 if (req->ir_replybuflen < i) 345 #else 346 if (req->ir_replybuflen < i && req->ir_replybuflen != 0) 347 #endif 348 device_printf(sc->ipmi_dev, 349 "SMIC: Read short: %zd buffer, %d actual\n", 350 req->ir_replybuflen, i); 351 return (1); 352 } 353 354 static void 355 smic_loop(void *arg) 356 { 357 struct ipmi_softc *sc = arg; 358 struct ipmi_request *req; 359 int i, ok; 360 361 IPMI_LOCK(sc); 362 while ((req = ipmi_dequeue_request(sc)) != NULL) { 363 IPMI_UNLOCK(sc); 364 ok = 0; 365 for (i = 0; i < 3 && !ok; i++) 366 ok = smic_polled_request(sc, req); 367 if (ok) 368 req->ir_error = 0; 369 else 370 req->ir_error = EIO; 371 IPMI_LOCK(sc); 372 ipmi_complete_request(sc, req); 373 } 374 IPMI_UNLOCK(sc); 375 kthread_exit(); 376 } 377 378 static int 379 smic_startup(struct ipmi_softc *sc) 380 { 381 382 return (kthread_create(smic_loop, sc, &sc->ipmi_kthread, 383 "%s: smic", device_get_nameunit(sc->ipmi_dev))); 384 } 385 386 int 387 ipmi_smic_attach(struct ipmi_softc *sc) 388 { 389 int flags; 390 391 /* Setup function pointers. */ 392 sc->ipmi_startup = smic_startup; 393 sc->ipmi_enqueue_request = ipmi_polled_enqueue_request; 394 395 /* See if we can talk to the controller. */ 396 flags = INB(sc, SMIC_FLAGS); 397 if (flags == 0xff) { 398 device_printf(sc->ipmi_dev, "couldn't find it\n"); 399 return (ENXIO); 400 } 401 402 #ifdef SMIC_DEBUG 403 device_printf(sc->ipmi_dev, "SMIC: initial state: %02x\n", flags); 404 #endif 405 406 return (0); 407 } 408