1162562Sjhb/*- 2162562Sjhb * Copyright (c) 2006 IronPort Systems Inc. <ambrisko@ironport.com> 3162562Sjhb * All rights reserved. 4162562Sjhb * 5162562Sjhb * Redistribution and use in source and binary forms, with or without 6162562Sjhb * modification, are permitted provided that the following conditions 7162562Sjhb * are met: 8162562Sjhb * 1. Redistributions of source code must retain the above copyright 9162562Sjhb * notice, this list of conditions and the following disclaimer. 10162562Sjhb * 2. Redistributions in binary form must reproduce the above copyright 11162562Sjhb * notice, this list of conditions and the following disclaimer in the 12162562Sjhb * documentation and/or other materials provided with the distribution. 13162562Sjhb * 14162562Sjhb * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15162562Sjhb * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16162562Sjhb * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17162562Sjhb * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18162562Sjhb * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19162562Sjhb * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20162562Sjhb * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21162562Sjhb * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22162562Sjhb * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23162562Sjhb * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24162562Sjhb * SUCH DAMAGE. 25162562Sjhb */ 26162562Sjhb 27162562Sjhb#include <sys/cdefs.h> 28162562Sjhb__FBSDID("$FreeBSD$"); 29162562Sjhb 30162562Sjhb#include <sys/param.h> 31162562Sjhb#include <sys/systm.h> 32162562Sjhb#include <sys/bus.h> 33162562Sjhb#include <sys/condvar.h> 34162562Sjhb#include <sys/eventhandler.h> 35162562Sjhb#include <sys/kernel.h> 36162562Sjhb#include <sys/kthread.h> 37162562Sjhb#include <sys/module.h> 38162562Sjhb#include <sys/rman.h> 39162562Sjhb#include <sys/selinfo.h> 40162562Sjhb#include <machine/bus.h> 41162562Sjhb 42162562Sjhb#ifdef LOCAL_MODULE 43162562Sjhb#include <ipmi.h> 44162562Sjhb#include <ipmivars.h> 45162562Sjhb#else 46162562Sjhb#include <sys/ipmi.h> 47162562Sjhb#include <dev/ipmi/ipmivars.h> 48162562Sjhb#endif 49162562Sjhb 50162562Sjhbstatic void smic_wait_for_tx_okay(struct ipmi_softc *); 51162562Sjhbstatic void smic_wait_for_rx_okay(struct ipmi_softc *); 52162562Sjhbstatic void smic_wait_for_not_busy(struct ipmi_softc *); 53162562Sjhbstatic void smic_set_busy(struct ipmi_softc *); 54162562Sjhb 55162562Sjhbstatic void 56162562Sjhbsmic_wait_for_tx_okay(struct ipmi_softc *sc) 57162562Sjhb{ 58162562Sjhb int flags; 59162562Sjhb 60162562Sjhb do { 61162562Sjhb flags = INB(sc, SMIC_FLAGS); 62162562Sjhb } while (!(flags & SMIC_STATUS_TX_RDY)); 63162562Sjhb} 64162562Sjhb 65162562Sjhbstatic void 66162562Sjhbsmic_wait_for_rx_okay(struct ipmi_softc *sc) 67162562Sjhb{ 68162562Sjhb int flags; 69162562Sjhb 70162562Sjhb do { 71162562Sjhb flags = INB(sc, SMIC_FLAGS); 72162562Sjhb } while (!(flags & SMIC_STATUS_RX_RDY)); 73162562Sjhb} 74162562Sjhb 75162562Sjhbstatic void 76162562Sjhbsmic_wait_for_not_busy(struct ipmi_softc *sc) 77162562Sjhb{ 78162562Sjhb int flags; 79162562Sjhb 80162562Sjhb do { 81162562Sjhb flags = INB(sc, SMIC_FLAGS); 82162562Sjhb } while (flags & SMIC_STATUS_BUSY); 83162562Sjhb} 84162562Sjhb 85162562Sjhbstatic void 86162562Sjhbsmic_set_busy(struct ipmi_softc *sc) 87162562Sjhb{ 88162562Sjhb int flags; 89162562Sjhb 90162562Sjhb flags = INB(sc, SMIC_FLAGS); 91162562Sjhb flags |= SMIC_STATUS_BUSY; 92162562Sjhb flags &= ~SMIC_STATUS_RESERVED; 93162562Sjhb OUTB(sc, SMIC_FLAGS, flags); 94162562Sjhb} 95162562Sjhb 96162562Sjhb/* 97162562Sjhb * Start a transfer with a WR_START transaction that sends the NetFn/LUN 98162562Sjhb * address. 99162562Sjhb */ 100162562Sjhbstatic int 101162562Sjhbsmic_start_write(struct ipmi_softc *sc, u_char data) 102162562Sjhb{ 103162562Sjhb u_char error, status; 104162562Sjhb 105162562Sjhb smic_wait_for_not_busy(sc); 106162562Sjhb 107162562Sjhb OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_WR_START); 108162562Sjhb OUTB(sc, SMIC_DATA, data); 109162562Sjhb smic_set_busy(sc); 110162562Sjhb smic_wait_for_not_busy(sc); 111162562Sjhb status = INB(sc, SMIC_CTL_STS); 112162562Sjhb if (status != SMIC_SC_SMS_WR_START) { 113162562Sjhb error = INB(sc, SMIC_DATA); 114162562Sjhb device_printf(sc->ipmi_dev, "SMIC: Write did not start %02x\n", 115162562Sjhb error); 116162562Sjhb return (0); 117162562Sjhb } 118162562Sjhb return (1); 119162562Sjhb} 120162562Sjhb 121162562Sjhb/* 122162562Sjhb * Write a byte in the middle of the message (either the command or one of 123162562Sjhb * the data bytes) using a WR_NEXT transaction. 124162562Sjhb */ 125162562Sjhbstatic int 126162562Sjhbsmic_write_next(struct ipmi_softc *sc, u_char data) 127162562Sjhb{ 128162562Sjhb u_char error, status; 129162562Sjhb 130163035Sjhb smic_wait_for_tx_okay(sc); 131162562Sjhb OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_WR_NEXT); 132162562Sjhb OUTB(sc, SMIC_DATA, data); 133162562Sjhb smic_set_busy(sc); 134162562Sjhb smic_wait_for_not_busy(sc); 135162562Sjhb status = INB(sc, SMIC_CTL_STS); 136162562Sjhb if (status != SMIC_SC_SMS_WR_NEXT) { 137162562Sjhb error = INB(sc, SMIC_DATA); 138162562Sjhb device_printf(sc->ipmi_dev, "SMIC: Write did not next %02x\n", 139162562Sjhb error); 140162562Sjhb return (0); 141162562Sjhb } 142162562Sjhb return (1); 143162562Sjhb} 144162562Sjhb 145162562Sjhb/* 146162562Sjhb * Write the last byte of a transfer to end the write phase via a WR_END 147162562Sjhb * transaction. 148162562Sjhb */ 149162562Sjhbstatic int 150162562Sjhbsmic_write_last(struct ipmi_softc *sc, u_char data) 151162562Sjhb{ 152162562Sjhb u_char error, status; 153162562Sjhb 154163035Sjhb smic_wait_for_tx_okay(sc); 155162562Sjhb OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_WR_END); 156162562Sjhb OUTB(sc, SMIC_DATA, data); 157162562Sjhb smic_set_busy(sc); 158162562Sjhb smic_wait_for_not_busy(sc); 159162562Sjhb status = INB(sc, SMIC_CTL_STS); 160162562Sjhb if (status != SMIC_SC_SMS_WR_END) { 161162562Sjhb error = INB(sc, SMIC_DATA); 162162562Sjhb device_printf(sc->ipmi_dev, "SMIC: Write did not end %02x\n", 163162562Sjhb error); 164162562Sjhb return (0); 165162562Sjhb } 166162562Sjhb return (1); 167162562Sjhb} 168162562Sjhb 169162562Sjhb/* 170162562Sjhb * Start the read phase of a transfer with a RD_START transaction. 171162562Sjhb */ 172162562Sjhbstatic int 173162562Sjhbsmic_start_read(struct ipmi_softc *sc, u_char *data) 174162562Sjhb{ 175162562Sjhb u_char error, status; 176162562Sjhb 177162562Sjhb smic_wait_for_not_busy(sc); 178162562Sjhb 179163035Sjhb smic_wait_for_rx_okay(sc); 180162562Sjhb OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_RD_START); 181162562Sjhb smic_set_busy(sc); 182162562Sjhb smic_wait_for_not_busy(sc); 183162562Sjhb status = INB(sc, SMIC_CTL_STS); 184162562Sjhb if (status != SMIC_SC_SMS_RD_START) { 185162562Sjhb error = INB(sc, SMIC_DATA); 186162562Sjhb device_printf(sc->ipmi_dev, "SMIC: Read did not start %02x\n", 187162562Sjhb error); 188162562Sjhb return (0); 189162562Sjhb } 190162562Sjhb *data = INB(sc, SMIC_DATA); 191162562Sjhb return (1); 192162562Sjhb} 193162562Sjhb 194162562Sjhb/* 195162562Sjhb * Read a byte via a RD_NEXT transaction. If this was the last byte, return 196162562Sjhb * 2 rather than 1. 197162562Sjhb */ 198162562Sjhbstatic int 199162562Sjhbsmic_read_byte(struct ipmi_softc *sc, u_char *data) 200162562Sjhb{ 201162562Sjhb u_char error, status; 202162562Sjhb 203163035Sjhb smic_wait_for_rx_okay(sc); 204162562Sjhb OUTB(sc, SMIC_CTL_STS, SMIC_SC_SMS_RD_NEXT); 205162562Sjhb smic_set_busy(sc); 206162562Sjhb smic_wait_for_not_busy(sc); 207162562Sjhb status = INB(sc, SMIC_CTL_STS); 208162562Sjhb if (status != SMIC_SC_SMS_RD_NEXT && 209162562Sjhb status != SMIC_SC_SMS_RD_END) { 210162562Sjhb error = INB(sc, SMIC_DATA); 211162562Sjhb device_printf(sc->ipmi_dev, "SMIC: Read did not next %02x\n", 212162562Sjhb error); 213162562Sjhb return (0); 214162562Sjhb } 215162562Sjhb *data = INB(sc, SMIC_DATA); 216162562Sjhb if (status == SMIC_SC_SMS_RD_NEXT) 217162562Sjhb return (1); 218162562Sjhb else 219162562Sjhb return (2); 220162562Sjhb} 221162562Sjhb 222162562Sjhb/* Complete a transfer via a RD_END transaction after reading the last byte. */ 223162562Sjhbstatic int 224162562Sjhbsmic_read_end(struct ipmi_softc *sc) 225162562Sjhb{ 226162562Sjhb u_char error, status; 227162562Sjhb 228162562Sjhb OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_RD_END); 229162562Sjhb smic_set_busy(sc); 230162562Sjhb smic_wait_for_not_busy(sc); 231162562Sjhb status = INB(sc, SMIC_CTL_STS); 232162562Sjhb if (status != SMIC_SC_SMS_RDY) { 233162562Sjhb error = INB(sc, SMIC_DATA); 234162562Sjhb device_printf(sc->ipmi_dev, "SMIC: Read did not end %02x\n", 235162562Sjhb error); 236162562Sjhb return (0); 237162562Sjhb } 238162562Sjhb return (1); 239162562Sjhb} 240162562Sjhb 241162562Sjhbstatic int 242162562Sjhbsmic_polled_request(struct ipmi_softc *sc, struct ipmi_request *req) 243162562Sjhb{ 244162562Sjhb u_char *cp, data; 245162562Sjhb int i, state; 246162562Sjhb 247162562Sjhb /* First, start the message with the address. */ 248162562Sjhb if (!smic_start_write(sc, req->ir_addr)) 249162562Sjhb return (0); 250163034Sjhb#ifdef SMIC_DEBUG 251163034Sjhb device_printf(sc->ipmi_dev, "SMIC: WRITE_START address: %02x\n", 252163034Sjhb req->ir_addr); 253163034Sjhb#endif 254162562Sjhb 255162562Sjhb if (req->ir_requestlen == 0) { 256162562Sjhb /* Send the command as the last byte. */ 257162562Sjhb if (!smic_write_last(sc, req->ir_command)) 258162562Sjhb return (0); 259163034Sjhb#ifdef SMIC_DEBUG 260163034Sjhb device_printf(sc->ipmi_dev, "SMIC: Wrote command: %02x\n", 261163034Sjhb req->ir_command); 262163034Sjhb#endif 263162562Sjhb } else { 264162562Sjhb /* Send the command. */ 265162562Sjhb if (!smic_write_next(sc, req->ir_command)) 266162562Sjhb return (0); 267163034Sjhb#ifdef SMIC_DEBUG 268163034Sjhb device_printf(sc->ipmi_dev, "SMIC: Wrote command: %02x\n", 269163034Sjhb req->ir_command); 270163034Sjhb#endif 271162562Sjhb 272162562Sjhb /* Send the payload. */ 273162562Sjhb cp = req->ir_request; 274163034Sjhb for (i = 0; i < req->ir_requestlen - 1; i++) { 275162562Sjhb if (!smic_write_next(sc, *cp++)) 276162562Sjhb return (0); 277163034Sjhb#ifdef SMIC_DEBUG 278163034Sjhb device_printf(sc->ipmi_dev, "SMIC: Wrote data: %02x\n", 279163034Sjhb cp[-1]); 280163034Sjhb#endif 281163034Sjhb } 282162562Sjhb if (!smic_write_last(sc, *cp)) 283162562Sjhb return (0); 284163034Sjhb#ifdef SMIC_DEBUG 285163034Sjhb device_printf(sc->ipmi_dev, "SMIC: Write last data: %02x\n", 286163034Sjhb *cp); 287163034Sjhb#endif 288162562Sjhb } 289162562Sjhb 290162562Sjhb /* Start the read phase by reading the NetFn/LUN. */ 291162562Sjhb if (smic_start_read(sc, &data) != 1) 292162562Sjhb return (0); 293163034Sjhb#ifdef SMIC_DEBUG 294163034Sjhb device_printf(sc->ipmi_dev, "SMIC: Read address: %02x\n", data); 295163034Sjhb#endif 296162562Sjhb if (data != IPMI_REPLY_ADDR(req->ir_addr)) { 297162562Sjhb device_printf(sc->ipmi_dev, "SMIC: Reply address mismatch\n"); 298162562Sjhb return (0); 299162562Sjhb } 300162562Sjhb 301162562Sjhb /* Read the command. */ 302162562Sjhb if (smic_read_byte(sc, &data) != 1) 303162562Sjhb return (0); 304163034Sjhb#ifdef SMIC_DEBUG 305163034Sjhb device_printf(sc->ipmi_dev, "SMIC: Read command: %02x\n", data); 306163034Sjhb#endif 307162562Sjhb if (data != req->ir_command) { 308162562Sjhb device_printf(sc->ipmi_dev, "SMIC: Command mismatch\n"); 309162562Sjhb return (0); 310162562Sjhb } 311162562Sjhb 312162562Sjhb /* Read the completion code. */ 313162562Sjhb state = smic_read_byte(sc, &req->ir_compcode); 314162562Sjhb if (state == 0) 315162562Sjhb return (0); 316163034Sjhb#ifdef SMIC_DEBUG 317163034Sjhb device_printf(sc->ipmi_dev, "SMIC: Read completion code: %02x\n", 318163034Sjhb req->ir_compcode); 319163034Sjhb#endif 320162562Sjhb 321162562Sjhb /* Finally, read the reply from the BMC. */ 322162562Sjhb i = 0; 323162562Sjhb while (state == 1) { 324162562Sjhb state = smic_read_byte(sc, &data); 325162562Sjhb if (state == 0) 326162562Sjhb return (0); 327163034Sjhb if (i < req->ir_replybuflen) { 328162562Sjhb req->ir_reply[i] = data; 329163034Sjhb#ifdef SMIC_DEBUG 330163034Sjhb device_printf(sc->ipmi_dev, "SMIC: Read data: %02x\n", 331163034Sjhb data); 332163034Sjhb } else { 333163034Sjhb device_printf(sc->ipmi_dev, 334163034Sjhb "SMIC: Read short %02x byte %d\n", data, i + 1); 335163034Sjhb#endif 336163034Sjhb } 337162562Sjhb i++; 338162562Sjhb } 339162562Sjhb 340162562Sjhb /* Terminate the transfer. */ 341162562Sjhb if (!smic_read_end(sc)) 342162562Sjhb return (0); 343162562Sjhb req->ir_replylen = i; 344163034Sjhb#ifdef SMIC_DEBUG 345163034Sjhb device_printf(sc->ipmi_dev, "SMIC: READ finished (%d bytes)\n", i); 346163034Sjhb if (req->ir_replybuflen < i) 347163034Sjhb#else 348162562Sjhb if (req->ir_replybuflen < i && req->ir_replybuflen != 0) 349163034Sjhb#endif 350162562Sjhb device_printf(sc->ipmi_dev, 351162562Sjhb "SMIC: Read short: %zd buffer, %d actual\n", 352162562Sjhb req->ir_replybuflen, i); 353162562Sjhb return (1); 354162562Sjhb} 355162562Sjhb 356162562Sjhbstatic void 357162562Sjhbsmic_loop(void *arg) 358162562Sjhb{ 359162562Sjhb struct ipmi_softc *sc = arg; 360162562Sjhb struct ipmi_request *req; 361162562Sjhb int i, ok; 362162562Sjhb 363162562Sjhb IPMI_LOCK(sc); 364162562Sjhb while ((req = ipmi_dequeue_request(sc)) != NULL) { 365248705Smelifaro IPMI_UNLOCK(sc); 366162562Sjhb ok = 0; 367278872Sscottl for (i = 0; i < 3 && !ok; i++) { 368278872Sscottl IPMI_IO_LOCK(sc); 369162562Sjhb ok = smic_polled_request(sc, req); 370278872Sscottl IPMI_IO_UNLOCK(sc); 371278872Sscottl } 372162562Sjhb if (ok) 373162562Sjhb req->ir_error = 0; 374162562Sjhb else 375162562Sjhb req->ir_error = EIO; 376248705Smelifaro IPMI_LOCK(sc); 377162562Sjhb ipmi_complete_request(sc, req); 378162562Sjhb } 379162562Sjhb IPMI_UNLOCK(sc); 380172836Sjulian kproc_exit(0); 381162562Sjhb} 382162562Sjhb 383162562Sjhbstatic int 384162562Sjhbsmic_startup(struct ipmi_softc *sc) 385162562Sjhb{ 386162562Sjhb 387172836Sjulian return (kproc_create(smic_loop, sc, &sc->ipmi_kthread, 0, 0, 388162562Sjhb "%s: smic", device_get_nameunit(sc->ipmi_dev))); 389162562Sjhb} 390162562Sjhb 391278872Sscottlstatic int 392278872Sscottlsmic_driver_request(struct ipmi_softc *sc, struct ipmi_request *req, int timo) 393278872Sscottl{ 394278872Sscottl int i, ok; 395278872Sscottl 396278872Sscottl ok = 0; 397278872Sscottl for (i = 0; i < 3 && !ok; i++) { 398278872Sscottl IPMI_IO_LOCK(sc); 399278872Sscottl ok = smic_polled_request(sc, req); 400278872Sscottl IPMI_IO_UNLOCK(sc); 401278872Sscottl } 402278872Sscottl if (ok) 403278872Sscottl req->ir_error = 0; 404278872Sscottl else 405278872Sscottl req->ir_error = EIO; 406278872Sscottl return (req->ir_error); 407278872Sscottl} 408278872Sscottl 409162562Sjhbint 410162562Sjhbipmi_smic_attach(struct ipmi_softc *sc) 411162562Sjhb{ 412162562Sjhb int flags; 413162562Sjhb 414162562Sjhb /* Setup function pointers. */ 415162562Sjhb sc->ipmi_startup = smic_startup; 416162562Sjhb sc->ipmi_enqueue_request = ipmi_polled_enqueue_request; 417278872Sscottl sc->ipmi_driver_request = smic_driver_request; 418287435Sjhb sc->ipmi_driver_requests_polled = 1; 419162562Sjhb 420162562Sjhb /* See if we can talk to the controller. */ 421162562Sjhb flags = INB(sc, SMIC_FLAGS); 422162562Sjhb if (flags == 0xff) { 423162562Sjhb device_printf(sc->ipmi_dev, "couldn't find it\n"); 424162562Sjhb return (ENXIO); 425162562Sjhb } 426162562Sjhb 427163034Sjhb#ifdef SMIC_DEBUG 428163034Sjhb device_printf(sc->ipmi_dev, "SMIC: initial state: %02x\n", flags); 429163034Sjhb#endif 430163034Sjhb 431162562Sjhb return (0); 432162562Sjhb} 433