ipmi_smic.c revision 248705
1130803Smarcel/*- 2130803Smarcel * Copyright (c) 2006 IronPort Systems Inc. <ambrisko@ironport.com> 3130803Smarcel * All rights reserved. 4130803Smarcel * 5130803Smarcel * Redistribution and use in source and binary forms, with or without 6130803Smarcel * modification, are permitted provided that the following conditions 7130803Smarcel * are met: 8130803Smarcel * 1. Redistributions of source code must retain the above copyright 9130803Smarcel * notice, this list of conditions and the following disclaimer. 10130803Smarcel * 2. Redistributions in binary form must reproduce the above copyright 11130803Smarcel * notice, this list of conditions and the following disclaimer in the 12130803Smarcel * documentation and/or other materials provided with the distribution. 13130803Smarcel * 14130803Smarcel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15130803Smarcel * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16130803Smarcel * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17130803Smarcel * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18130803Smarcel * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19130803Smarcel * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20130803Smarcel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21130803Smarcel * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22130803Smarcel * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23130803Smarcel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24130803Smarcel * SUCH DAMAGE. 25130803Smarcel */ 26130803Smarcel 27130803Smarcel#include <sys/cdefs.h> 28130803Smarcel__FBSDID("$FreeBSD: head/sys/dev/ipmi/ipmi_smic.c 248705 2013-03-25 14:30:34Z melifaro $"); 29130803Smarcel 30130803Smarcel#include <sys/param.h> 31130803Smarcel#include <sys/systm.h> 32130803Smarcel#include <sys/bus.h> 33130803Smarcel#include <sys/condvar.h> 34130803Smarcel#include <sys/eventhandler.h> 35130803Smarcel#include <sys/kernel.h> 36130803Smarcel#include <sys/kthread.h> 37130803Smarcel#include <sys/module.h> 38130803Smarcel#include <sys/rman.h> 39130803Smarcel#include <sys/selinfo.h> 40130803Smarcel#include <machine/bus.h> 41130803Smarcel 42130803Smarcel#ifdef LOCAL_MODULE 43130803Smarcel#include <ipmi.h> 44130803Smarcel#include <ipmivars.h> 45130803Smarcel#else 46130803Smarcel#include <sys/ipmi.h> 47130803Smarcel#include <dev/ipmi/ipmivars.h> 48130803Smarcel#endif 49130803Smarcel 50130803Smarcelstatic void smic_wait_for_tx_okay(struct ipmi_softc *); 51130803Smarcelstatic void smic_wait_for_rx_okay(struct ipmi_softc *); 52130803Smarcelstatic void smic_wait_for_not_busy(struct ipmi_softc *); 53130803Smarcelstatic void smic_set_busy(struct ipmi_softc *); 54130803Smarcel 55130803Smarcelstatic void 56130803Smarcelsmic_wait_for_tx_okay(struct ipmi_softc *sc) 57130803Smarcel{ 58130803Smarcel int flags; 59130803Smarcel 60130803Smarcel do { 61130803Smarcel flags = INB(sc, SMIC_FLAGS); 62130803Smarcel } while (!(flags & SMIC_STATUS_TX_RDY)); 63130803Smarcel} 64130803Smarcel 65130803Smarcelstatic void 66130803Smarcelsmic_wait_for_rx_okay(struct ipmi_softc *sc) 67130803Smarcel{ 68130803Smarcel int flags; 69130803Smarcel 70130803Smarcel do { 71130803Smarcel flags = INB(sc, SMIC_FLAGS); 72130803Smarcel } while (!(flags & SMIC_STATUS_RX_RDY)); 73130803Smarcel} 74130803Smarcel 75130803Smarcelstatic void 76130803Smarcelsmic_wait_for_not_busy(struct ipmi_softc *sc) 77130803Smarcel{ 78130803Smarcel int flags; 79130803Smarcel 80130803Smarcel do { 81130803Smarcel flags = INB(sc, SMIC_FLAGS); 82130803Smarcel } while (flags & SMIC_STATUS_BUSY); 83130803Smarcel} 84130803Smarcel 85130803Smarcelstatic void 86130803Smarcelsmic_set_busy(struct ipmi_softc *sc) 87130803Smarcel{ 88130803Smarcel int flags; 89130803Smarcel 90130803Smarcel flags = INB(sc, SMIC_FLAGS); 91130803Smarcel flags |= SMIC_STATUS_BUSY; 92130803Smarcel flags &= ~SMIC_STATUS_RESERVED; 93130803Smarcel OUTB(sc, SMIC_FLAGS, flags); 94130803Smarcel} 95130803Smarcel 96130803Smarcel/* 97130803Smarcel * Start a transfer with a WR_START transaction that sends the NetFn/LUN 98130803Smarcel * address. 99130803Smarcel */ 100130803Smarcelstatic int 101130803Smarcelsmic_start_write(struct ipmi_softc *sc, u_char data) 102130803Smarcel{ 103130803Smarcel u_char error, status; 104130803Smarcel 105130803Smarcel smic_wait_for_not_busy(sc); 106130803Smarcel 107130803Smarcel OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_WR_START); 108130803Smarcel OUTB(sc, SMIC_DATA, data); 109130803Smarcel smic_set_busy(sc); 110130803Smarcel smic_wait_for_not_busy(sc); 111130803Smarcel status = INB(sc, SMIC_CTL_STS); 112130803Smarcel if (status != SMIC_SC_SMS_WR_START) { 113130803Smarcel error = INB(sc, SMIC_DATA); 114130803Smarcel device_printf(sc->ipmi_dev, "SMIC: Write did not start %02x\n", 115130803Smarcel error); 116130803Smarcel return (0); 117130803Smarcel } 118130803Smarcel return (1); 119130803Smarcel} 120130803Smarcel 121130803Smarcel/* 122130803Smarcel * Write a byte in the middle of the message (either the command or one of 123130803Smarcel * the data bytes) using a WR_NEXT transaction. 124130803Smarcel */ 125130803Smarcelstatic int 126130803Smarcelsmic_write_next(struct ipmi_softc *sc, u_char data) 127130803Smarcel{ 128130803Smarcel u_char error, status; 129130803Smarcel 130130803Smarcel smic_wait_for_tx_okay(sc); 131130803Smarcel OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_WR_NEXT); 132130803Smarcel OUTB(sc, SMIC_DATA, data); 133130803Smarcel smic_set_busy(sc); 134130803Smarcel smic_wait_for_not_busy(sc); 135130803Smarcel status = INB(sc, SMIC_CTL_STS); 136130803Smarcel if (status != SMIC_SC_SMS_WR_NEXT) { 137130803Smarcel error = INB(sc, SMIC_DATA); 138130803Smarcel device_printf(sc->ipmi_dev, "SMIC: Write did not next %02x\n", 139130803Smarcel error); 140130803Smarcel return (0); 141130803Smarcel } 142130803Smarcel return (1); 143130803Smarcel} 144130803Smarcel 145130803Smarcel/* 146130803Smarcel * Write the last byte of a transfer to end the write phase via a WR_END 147130803Smarcel * transaction. 148130803Smarcel */ 149130803Smarcelstatic int 150130803Smarcelsmic_write_last(struct ipmi_softc *sc, u_char data) 151130803Smarcel{ 152130803Smarcel u_char error, status; 153130803Smarcel 154130803Smarcel smic_wait_for_tx_okay(sc); 155130803Smarcel OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_WR_END); 156130803Smarcel OUTB(sc, SMIC_DATA, data); 157130803Smarcel smic_set_busy(sc); 158130803Smarcel smic_wait_for_not_busy(sc); 159130803Smarcel status = INB(sc, SMIC_CTL_STS); 160130803Smarcel if (status != SMIC_SC_SMS_WR_END) { 161130803Smarcel error = INB(sc, SMIC_DATA); 162130803Smarcel device_printf(sc->ipmi_dev, "SMIC: Write did not end %02x\n", 163130803Smarcel error); 164130803Smarcel return (0); 165130803Smarcel } 166130803Smarcel return (1); 167130803Smarcel} 168130803Smarcel 169130803Smarcel/* 170130803Smarcel * Start the read phase of a transfer with a RD_START transaction. 171130803Smarcel */ 172130803Smarcelstatic int 173130803Smarcelsmic_start_read(struct ipmi_softc *sc, u_char *data) 174130803Smarcel{ 175130803Smarcel u_char error, status; 176130803Smarcel 177130803Smarcel smic_wait_for_not_busy(sc); 178130803Smarcel 179130803Smarcel smic_wait_for_rx_okay(sc); 180130803Smarcel OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_RD_START); 181130803Smarcel smic_set_busy(sc); 182130803Smarcel smic_wait_for_not_busy(sc); 183130803Smarcel status = INB(sc, SMIC_CTL_STS); 184130803Smarcel if (status != SMIC_SC_SMS_RD_START) { 185130803Smarcel error = INB(sc, SMIC_DATA); 186130803Smarcel device_printf(sc->ipmi_dev, "SMIC: Read did not start %02x\n", 187130803Smarcel error); 188130803Smarcel return (0); 189130803Smarcel } 190130803Smarcel *data = INB(sc, SMIC_DATA); 191130803Smarcel return (1); 192130803Smarcel} 193130803Smarcel 194130803Smarcel/* 195130803Smarcel * Read a byte via a RD_NEXT transaction. If this was the last byte, return 196130803Smarcel * 2 rather than 1. 197130803Smarcel */ 198130803Smarcelstatic int 199130803Smarcelsmic_read_byte(struct ipmi_softc *sc, u_char *data) 200130803Smarcel{ 201130803Smarcel u_char error, status; 202130803Smarcel 203130803Smarcel smic_wait_for_rx_okay(sc); 204130803Smarcel OUTB(sc, SMIC_CTL_STS, SMIC_SC_SMS_RD_NEXT); 205130803Smarcel smic_set_busy(sc); 206130803Smarcel smic_wait_for_not_busy(sc); 207130803Smarcel status = INB(sc, SMIC_CTL_STS); 208130803Smarcel if (status != SMIC_SC_SMS_RD_NEXT && 209130803Smarcel status != SMIC_SC_SMS_RD_END) { 210130803Smarcel error = INB(sc, SMIC_DATA); 211130803Smarcel device_printf(sc->ipmi_dev, "SMIC: Read did not next %02x\n", 212130803Smarcel error); 213130803Smarcel return (0); 214130803Smarcel } 215130803Smarcel *data = INB(sc, SMIC_DATA); 216130803Smarcel if (status == SMIC_SC_SMS_RD_NEXT) 217130803Smarcel return (1); 218130803Smarcel else 219130803Smarcel return (2); 220130803Smarcel} 221130803Smarcel 222130803Smarcel/* Complete a transfer via a RD_END transaction after reading the last byte. */ 223130803Smarcelstatic int 224130803Smarcelsmic_read_end(struct ipmi_softc *sc) 225130803Smarcel{ 226130803Smarcel u_char error, status; 227130803Smarcel 228130803Smarcel OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_RD_END); 229130803Smarcel smic_set_busy(sc); 230130803Smarcel smic_wait_for_not_busy(sc); 231130803Smarcel status = INB(sc, SMIC_CTL_STS); 232130803Smarcel if (status != SMIC_SC_SMS_RDY) { 233130803Smarcel error = INB(sc, SMIC_DATA); 234130803Smarcel device_printf(sc->ipmi_dev, "SMIC: Read did not end %02x\n", 235130803Smarcel error); 236130803Smarcel return (0); 237130803Smarcel } 238130803Smarcel return (1); 239130803Smarcel} 240130803Smarcel 241130803Smarcelstatic int 242130803Smarcelsmic_polled_request(struct ipmi_softc *sc, struct ipmi_request *req) 243130803Smarcel{ 244130803Smarcel u_char *cp, data; 245130803Smarcel int i, state; 246130803Smarcel 247130803Smarcel /* First, start the message with the address. */ 248130803Smarcel if (!smic_start_write(sc, req->ir_addr)) 249130803Smarcel return (0); 250130803Smarcel#ifdef SMIC_DEBUG 251130803Smarcel device_printf(sc->ipmi_dev, "SMIC: WRITE_START address: %02x\n", 252130803Smarcel req->ir_addr); 253130803Smarcel#endif 254130803Smarcel 255130803Smarcel if (req->ir_requestlen == 0) { 256130803Smarcel /* Send the command as the last byte. */ 257130803Smarcel if (!smic_write_last(sc, req->ir_command)) 258130803Smarcel return (0); 259130803Smarcel#ifdef SMIC_DEBUG 260130803Smarcel device_printf(sc->ipmi_dev, "SMIC: Wrote command: %02x\n", 261130803Smarcel req->ir_command); 262130803Smarcel#endif 263130803Smarcel } else { 264130803Smarcel /* Send the command. */ 265130803Smarcel if (!smic_write_next(sc, req->ir_command)) 266130803Smarcel return (0); 267130803Smarcel#ifdef SMIC_DEBUG 268130803Smarcel device_printf(sc->ipmi_dev, "SMIC: Wrote command: %02x\n", 269130803Smarcel req->ir_command); 270130803Smarcel#endif 271130803Smarcel 272130803Smarcel /* Send the payload. */ 273130803Smarcel cp = req->ir_request; 274130803Smarcel for (i = 0; i < req->ir_requestlen - 1; i++) { 275130803Smarcel if (!smic_write_next(sc, *cp++)) 276130803Smarcel return (0); 277130803Smarcel#ifdef SMIC_DEBUG 278130803Smarcel device_printf(sc->ipmi_dev, "SMIC: Wrote data: %02x\n", 279130803Smarcel cp[-1]); 280130803Smarcel#endif 281130803Smarcel } 282130803Smarcel if (!smic_write_last(sc, *cp)) 283130803Smarcel return (0); 284130803Smarcel#ifdef SMIC_DEBUG 285130803Smarcel device_printf(sc->ipmi_dev, "SMIC: Write last data: %02x\n", 286130803Smarcel *cp); 287130803Smarcel#endif 288130803Smarcel } 289130803Smarcel 290130803Smarcel /* Start the read phase by reading the NetFn/LUN. */ 291130803Smarcel if (smic_start_read(sc, &data) != 1) 292130803Smarcel return (0); 293130803Smarcel#ifdef SMIC_DEBUG 294130803Smarcel device_printf(sc->ipmi_dev, "SMIC: Read address: %02x\n", data); 295130803Smarcel#endif 296130803Smarcel if (data != IPMI_REPLY_ADDR(req->ir_addr)) { 297130803Smarcel device_printf(sc->ipmi_dev, "SMIC: Reply address mismatch\n"); 298130803Smarcel return (0); 299130803Smarcel } 300130803Smarcel 301130803Smarcel /* Read the command. */ 302130803Smarcel if (smic_read_byte(sc, &data) != 1) 303130803Smarcel return (0); 304130803Smarcel#ifdef SMIC_DEBUG 305130803Smarcel device_printf(sc->ipmi_dev, "SMIC: Read command: %02x\n", data); 306130803Smarcel#endif 307130803Smarcel if (data != req->ir_command) { 308130803Smarcel device_printf(sc->ipmi_dev, "SMIC: Command mismatch\n"); 309130803Smarcel return (0); 310130803Smarcel } 311130803Smarcel 312130803Smarcel /* Read the completion code. */ 313130803Smarcel state = smic_read_byte(sc, &req->ir_compcode); 314130803Smarcel if (state == 0) 315130803Smarcel return (0); 316130803Smarcel#ifdef SMIC_DEBUG 317130803Smarcel device_printf(sc->ipmi_dev, "SMIC: Read completion code: %02x\n", 318130803Smarcel req->ir_compcode); 319130803Smarcel#endif 320130803Smarcel 321130803Smarcel /* Finally, read the reply from the BMC. */ 322130803Smarcel i = 0; 323130803Smarcel while (state == 1) { 324130803Smarcel state = smic_read_byte(sc, &data); 325130803Smarcel if (state == 0) 326130803Smarcel return (0); 327130803Smarcel if (i < req->ir_replybuflen) { 328130803Smarcel req->ir_reply[i] = data; 329130803Smarcel#ifdef SMIC_DEBUG 330130803Smarcel device_printf(sc->ipmi_dev, "SMIC: Read data: %02x\n", 331130803Smarcel data); 332130803Smarcel } else { 333130803Smarcel device_printf(sc->ipmi_dev, 334130803Smarcel "SMIC: Read short %02x byte %d\n", data, i + 1); 335130803Smarcel#endif 336130803Smarcel } 337130803Smarcel i++; 338130803Smarcel } 339130803Smarcel 340130803Smarcel /* Terminate the transfer. */ 341130803Smarcel if (!smic_read_end(sc)) 342130803Smarcel return (0); 343130803Smarcel req->ir_replylen = i; 344130803Smarcel#ifdef SMIC_DEBUG 345130803Smarcel device_printf(sc->ipmi_dev, "SMIC: READ finished (%d bytes)\n", i); 346130803Smarcel if (req->ir_replybuflen < i) 347130803Smarcel#else 348130803Smarcel if (req->ir_replybuflen < i && req->ir_replybuflen != 0) 349130803Smarcel#endif 350130803Smarcel device_printf(sc->ipmi_dev, 351130803Smarcel "SMIC: Read short: %zd buffer, %d actual\n", 352130803Smarcel req->ir_replybuflen, i); 353130803Smarcel return (1); 354130803Smarcel} 355130803Smarcel 356130803Smarcelstatic void 357130803Smarcelsmic_loop(void *arg) 358130803Smarcel{ 359130803Smarcel struct ipmi_softc *sc = arg; 360130803Smarcel struct ipmi_request *req; 361130803Smarcel int i, ok; 362130803Smarcel 363130803Smarcel IPMI_LOCK(sc); 364130803Smarcel while ((req = ipmi_dequeue_request(sc)) != NULL) { 365130803Smarcel IPMI_UNLOCK(sc); 366130803Smarcel ok = 0; 367130803Smarcel for (i = 0; i < 3 && !ok; i++) 368130803Smarcel ok = smic_polled_request(sc, req); 369130803Smarcel if (ok) 370130803Smarcel req->ir_error = 0; 371130803Smarcel else 372130803Smarcel req->ir_error = EIO; 373130803Smarcel IPMI_LOCK(sc); 374130803Smarcel ipmi_complete_request(sc, req); 375130803Smarcel } 376130803Smarcel IPMI_UNLOCK(sc); 377130803Smarcel kproc_exit(0); 378130803Smarcel} 379130803Smarcel 380130803Smarcelstatic int 381130803Smarcelsmic_startup(struct ipmi_softc *sc) 382130803Smarcel{ 383130803Smarcel 384130803Smarcel return (kproc_create(smic_loop, sc, &sc->ipmi_kthread, 0, 0, 385130803Smarcel "%s: smic", device_get_nameunit(sc->ipmi_dev))); 386130803Smarcel} 387130803Smarcel 388130803Smarcelint 389130803Smarcelipmi_smic_attach(struct ipmi_softc *sc) 390130803Smarcel{ 391130803Smarcel int flags; 392130803Smarcel 393130803Smarcel /* Setup function pointers. */ 394130803Smarcel sc->ipmi_startup = smic_startup; 395130803Smarcel sc->ipmi_enqueue_request = ipmi_polled_enqueue_request; 396130803Smarcel 397130803Smarcel /* See if we can talk to the controller. */ 398130803Smarcel flags = INB(sc, SMIC_FLAGS); 399130803Smarcel if (flags == 0xff) { 400130803Smarcel device_printf(sc->ipmi_dev, "couldn't find it\n"); 401130803Smarcel return (ENXIO); 402130803Smarcel } 403130803Smarcel 404130803Smarcel#ifdef SMIC_DEBUG 405130803Smarcel device_printf(sc->ipmi_dev, "SMIC: initial state: %02x\n", flags); 406130803Smarcel#endif 407130803Smarcel 408130803Smarcel return (0); 409130803Smarcel} 410130803Smarcel