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: releng/10.2/sys/dev/ipmi/ipmi_ssif.c 278872 2015-02-16 22:33:44Z scottl $"); 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/selinfo.h> 39162562Sjhb 40162562Sjhb#include <dev/smbus/smbconf.h> 41162562Sjhb#include <dev/smbus/smb.h> 42162562Sjhb 43162562Sjhb#include "smbus_if.h" 44162562Sjhb 45162562Sjhb#ifdef LOCAL_MODULE 46162562Sjhb#include <ipmivars.h> 47162562Sjhb#else 48162562Sjhb#include <dev/ipmi/ipmivars.h> 49162562Sjhb#endif 50162562Sjhb 51162562Sjhb#define SMBUS_WRITE_SINGLE 0x02 52162562Sjhb#define SMBUS_WRITE_START 0x06 53162562Sjhb#define SMBUS_WRITE_CONT 0x07 54162562Sjhb#define SMBUS_READ_START 0x03 55162562Sjhb#define SMBUS_READ_CONT 0x09 56162562Sjhb#define SMBUS_DATA_SIZE 32 57162562Sjhb 58162562Sjhb#ifdef SSIF_DEBUG 59162562Sjhbstatic void 60162562Sjhbdump_buffer(device_t dev, const char *msg, u_char *bytes, int len) 61162562Sjhb{ 62162562Sjhb int i; 63162562Sjhb 64162562Sjhb device_printf(dev, "%s:", msg); 65162562Sjhb for (i = 0; i < len; i++) 66162562Sjhb printf(" %02x", bytes[i]); 67162562Sjhb printf("\n"); 68162562Sjhb} 69162562Sjhb#endif 70162562Sjhb 71162562Sjhbstatic int 72162562Sjhbssif_polled_request(struct ipmi_softc *sc, struct ipmi_request *req) 73162562Sjhb{ 74162562Sjhb u_char ssif_buf[SMBUS_DATA_SIZE]; 75162562Sjhb device_t dev = sc->ipmi_dev; 76162562Sjhb device_t smbus = sc->ipmi_ssif_smbus; 77162562Sjhb u_char *cp, block, count, offset; 78162562Sjhb size_t len; 79162562Sjhb int error; 80162562Sjhb 81162562Sjhb /* Acquire the bus while we send the request. */ 82162562Sjhb if (smbus_request_bus(smbus, dev, SMB_WAIT) != 0) 83162562Sjhb return (0); 84162562Sjhb 85162562Sjhb /* 86162562Sjhb * First, send out the request. Begin by filling out the first 87162562Sjhb * packet which includes the NetFn/LUN and command. 88162562Sjhb */ 89162562Sjhb ssif_buf[0] = req->ir_addr; 90162562Sjhb ssif_buf[1] = req->ir_command; 91162562Sjhb if (req->ir_requestlen > 0) 92162562Sjhb bcopy(req->ir_request, &ssif_buf[2], 93162562Sjhb min(req->ir_requestlen, SMBUS_DATA_SIZE - 2)); 94162562Sjhb 95162562Sjhb /* Small requests are sent with a single command. */ 96162562Sjhb if (req->ir_requestlen <= 30) { 97162562Sjhb#ifdef SSIF_DEBUG 98162562Sjhb dump_buffer(dev, "WRITE_SINGLE", ssif_buf, 99162562Sjhb req->ir_requestlen + 2); 100162562Sjhb#endif 101162562Sjhb error = smbus_error(smbus_bwrite(smbus, 102162562Sjhb sc->ipmi_ssif_smbus_address, SMBUS_WRITE_SINGLE, 103162562Sjhb req->ir_requestlen + 2, ssif_buf)); 104162562Sjhb if (error) { 105162562Sjhb#ifdef SSIF_ERROR_DEBUG 106162562Sjhb device_printf(dev, "SSIF: WRITE_SINGLE error %d\n", 107162562Sjhb error); 108162562Sjhb#endif 109162562Sjhb goto fail; 110162562Sjhb } 111162562Sjhb } else { 112162562Sjhb /* Longer requests are sent out in 32-byte messages. */ 113162562Sjhb#ifdef SSIF_DEBUG 114162562Sjhb dump_buffer(dev, "WRITE_START", ssif_buf, SMBUS_DATA_SIZE); 115162562Sjhb#endif 116162562Sjhb error = smbus_error(smbus_bwrite(smbus, 117162562Sjhb sc->ipmi_ssif_smbus_address, SMBUS_WRITE_START, 118162562Sjhb SMBUS_DATA_SIZE, ssif_buf)); 119162562Sjhb if (error) { 120162562Sjhb#ifdef SSIF_ERROR_DEBUG 121162562Sjhb device_printf(dev, "SSIF: WRITE_START error %d\n", 122162562Sjhb error); 123162562Sjhb#endif 124162562Sjhb goto fail; 125162562Sjhb } 126162562Sjhb 127162562Sjhb len = req->ir_requestlen - (SMBUS_DATA_SIZE - 2); 128162562Sjhb cp = req->ir_request + (SMBUS_DATA_SIZE - 2); 129162562Sjhb while (len > 0) { 130162562Sjhb#ifdef SSIF_DEBUG 131162562Sjhb dump_buffer(dev, "WRITE_CONT", cp, 132162562Sjhb min(len, SMBUS_DATA_SIZE)); 133162562Sjhb#endif 134162562Sjhb error = smbus_error(smbus_bwrite(smbus, 135162562Sjhb sc->ipmi_ssif_smbus_address, SMBUS_WRITE_CONT, 136162562Sjhb min(len, SMBUS_DATA_SIZE), cp)); 137162562Sjhb if (error) { 138162562Sjhb#ifdef SSIF_ERROR_DEBUG 139162562Sjhb device_printf(dev, "SSIF: WRITE_CONT error %d\n", 140162562Sjhb error); 141162562Sjhb#endif 142162562Sjhb goto fail; 143162562Sjhb } 144162562Sjhb cp += SMBUS_DATA_SIZE; 145162562Sjhb len -= SMBUS_DATA_SIZE; 146162562Sjhb } 147162562Sjhb 148162562Sjhb /* 149162562Sjhb * The final WRITE_CONT transaction has to have a non-zero 150162562Sjhb * length that is also not SMBUS_DATA_SIZE. If our last 151162562Sjhb * WRITE_CONT transaction in the loop sent SMBUS_DATA_SIZE 152162562Sjhb * bytes, then len will be 0, and we send an extra 0x00 byte 153162562Sjhb * to terminate the transaction. 154162562Sjhb */ 155162562Sjhb if (len == 0) { 156162562Sjhb char c = 0; 157162562Sjhb 158162562Sjhb#ifdef SSIF_DEBUG 159162562Sjhb dump_buffer(dev, "WRITE_CONT", &c, 1); 160162562Sjhb#endif 161162562Sjhb error = smbus_error(smbus_bwrite(smbus, 162162562Sjhb sc->ipmi_ssif_smbus_address, SMBUS_WRITE_CONT, 163162562Sjhb 1, &c)); 164162562Sjhb if (error) { 165162562Sjhb#ifdef SSIF_ERROR_DEBUG 166162562Sjhb device_printf(dev, "SSIF: WRITE_CONT error %d\n", 167162562Sjhb error); 168162562Sjhb#endif 169162562Sjhb goto fail; 170162562Sjhb } 171162562Sjhb } 172162562Sjhb } 173162562Sjhb 174162562Sjhb /* Release the bus. */ 175162562Sjhb smbus_release_bus(smbus, dev); 176162562Sjhb 177162562Sjhb /* Give the BMC 100ms to chew on the request. */ 178167086Sjhb pause("ssifwt", hz / 10); 179162562Sjhb 180162562Sjhb /* Try to read the first packet. */ 181162562Sjhbread_start: 182162562Sjhb if (smbus_request_bus(smbus, dev, SMB_WAIT) != 0) 183162562Sjhb return (0); 184162562Sjhb count = SMBUS_DATA_SIZE; 185162562Sjhb error = smbus_error(smbus_bread(smbus, 186162562Sjhb sc->ipmi_ssif_smbus_address, SMBUS_READ_START, &count, ssif_buf)); 187162562Sjhb if (error == ENXIO || error == EBUSY) { 188162562Sjhb smbus_release_bus(smbus, dev); 189162562Sjhb#ifdef SSIF_DEBUG 190162562Sjhb device_printf(dev, "SSIF: READ_START retry\n"); 191162562Sjhb#endif 192162562Sjhb /* Give the BMC another 10ms. */ 193167086Sjhb pause("ssifwt", hz / 100); 194162562Sjhb goto read_start; 195162562Sjhb } 196162562Sjhb if (error) { 197162562Sjhb#ifdef SSIF_ERROR_DEBUG 198162562Sjhb device_printf(dev, "SSIF: READ_START failed: %d\n", error); 199162562Sjhb#endif 200162562Sjhb goto fail; 201162562Sjhb } 202162562Sjhb#ifdef SSIF_DEBUG 203162562Sjhb device_printf("SSIF: READ_START: ok\n"); 204162562Sjhb#endif 205162562Sjhb 206162562Sjhb /* 207162562Sjhb * If this is the first part of a multi-part read, then we need to 208162562Sjhb * skip the first two bytes. 209162562Sjhb */ 210162562Sjhb if (count == SMBUS_DATA_SIZE && ssif_buf[0] == 0 && ssif_buf[1] == 1) 211162562Sjhb offset = 2; 212162562Sjhb else 213162562Sjhb offset = 0; 214162562Sjhb 215162562Sjhb /* We had better get the reply header. */ 216162562Sjhb if (count < 3) { 217162562Sjhb device_printf(dev, "SSIF: Short reply packet\n"); 218162562Sjhb goto fail; 219162562Sjhb } 220162562Sjhb 221162562Sjhb /* Verify the NetFn/LUN. */ 222162562Sjhb if (ssif_buf[offset] != IPMI_REPLY_ADDR(req->ir_addr)) { 223162562Sjhb device_printf(dev, "SSIF: Reply address mismatch\n"); 224162562Sjhb goto fail; 225162562Sjhb } 226162562Sjhb 227162562Sjhb /* Verify the command. */ 228162562Sjhb if (ssif_buf[offset + 1] != req->ir_command) { 229162562Sjhb device_printf(dev, "SMIC: Command mismatch\n"); 230162562Sjhb goto fail; 231162562Sjhb } 232162562Sjhb 233162562Sjhb /* Read the completion code. */ 234162562Sjhb req->ir_compcode = ssif_buf[offset + 2]; 235162562Sjhb 236162562Sjhb /* If this is a single read, just copy the data and return. */ 237162562Sjhb if (offset == 0) { 238162562Sjhb#ifdef SSIF_DEBUG 239162562Sjhb dump_buffer(dev, "READ_SINGLE", ssif_buf, count); 240162562Sjhb#endif 241162562Sjhb len = count - 3; 242162562Sjhb bcopy(&ssif_buf[3], req->ir_reply, 243162562Sjhb min(req->ir_replybuflen, len)); 244162562Sjhb goto done; 245162562Sjhb } 246162562Sjhb 247162562Sjhb /* 248162562Sjhb * This is the first part of a multi-read transaction, so copy 249162562Sjhb * out the payload and start looping. 250162562Sjhb */ 251162562Sjhb#ifdef SSIF_DEBUG 252162562Sjhb dump_buffer(dev, "READ_START", ssif_buf + 2, count - 2); 253162562Sjhb#endif 254162562Sjhb bcopy(&ssif_buf[5], req->ir_reply, min(req->ir_replybuflen, count - 5)); 255162562Sjhb len = count - 5; 256162562Sjhb block = 1; 257162562Sjhb 258162562Sjhb for (;;) { 259162562Sjhb /* Read another packet via READ_CONT. */ 260162562Sjhb count = SMBUS_DATA_SIZE; 261162562Sjhb error = smbus_error(smbus_bread(smbus, 262162562Sjhb sc->ipmi_ssif_smbus_address, SMBUS_READ_CONT, &count, 263162562Sjhb ssif_buf)); 264162562Sjhb if (error) { 265162562Sjhb#ifdef SSIF_ERROR_DEBUG 266162562Sjhb printf("SSIF: READ_CONT failed: %d\n", error); 267162562Sjhb#endif 268162562Sjhb goto fail; 269162562Sjhb } 270162562Sjhb#ifdef SSIF_DEBUG 271162562Sjhb device_printf(dev, "SSIF: READ_CONT... ok\n"); 272162562Sjhb#endif 273162562Sjhb 274162562Sjhb /* Verify the block number. 0xff marks the last block. */ 275162562Sjhb if (ssif_buf[0] != 0xff && ssif_buf[0] != block) { 276162562Sjhb device_printf(dev, "SSIF: Read wrong block %d %d\n", 277162562Sjhb ssif_buf[0], block); 278162562Sjhb goto fail; 279162562Sjhb } 280162562Sjhb if (ssif_buf[0] != 0xff && count < SMBUS_DATA_SIZE) { 281162562Sjhb device_printf(dev, 282162562Sjhb "SSIF: Read short middle block, length %d\n", 283162562Sjhb count); 284162562Sjhb goto fail; 285162562Sjhb } 286162562Sjhb#ifdef SSIF_DEBUG 287162562Sjhb if (ssif_buf[0] == 0xff) 288162562Sjhb dump_buffer(dev, "READ_END", ssif_buf + 1, count - 1); 289162562Sjhb else 290162562Sjhb dump_buffer(dev, "READ_CONT", ssif_buf + 1, count - 1); 291162562Sjhb#endif 292162562Sjhb if (len < req->ir_replybuflen) 293162562Sjhb bcopy(&ssif_buf[1], &req->ir_reply[len], 294162562Sjhb min(req->ir_replybuflen - len, count - 1)); 295162562Sjhb len += count - 1; 296162562Sjhb 297162562Sjhb /* If this was the last block we are done. */ 298162562Sjhb if (ssif_buf[0] != 0xff) 299162562Sjhb break; 300162562Sjhb block++; 301162562Sjhb } 302162562Sjhb 303162562Sjhbdone: 304162562Sjhb /* Save the total length and return success. */ 305162562Sjhb req->ir_replylen = len; 306162562Sjhb smbus_release_bus(smbus, dev); 307162562Sjhb return (1); 308162562Sjhb 309162562Sjhbfail: 310162562Sjhb smbus_release_bus(smbus, dev); 311162562Sjhb return (0); 312162562Sjhb} 313162562Sjhb 314162562Sjhbstatic void 315162562Sjhbssif_loop(void *arg) 316162562Sjhb{ 317162562Sjhb struct ipmi_softc *sc = arg; 318162562Sjhb struct ipmi_request *req; 319162562Sjhb int i, ok; 320162562Sjhb 321162562Sjhb IPMI_LOCK(sc); 322162562Sjhb while ((req = ipmi_dequeue_request(sc)) != NULL) { 323162562Sjhb IPMI_UNLOCK(sc); 324162562Sjhb ok = 0; 325162562Sjhb for (i = 0; i < 5; i++) { 326162562Sjhb ok = ssif_polled_request(sc, req); 327162562Sjhb if (ok) 328162562Sjhb break; 329162562Sjhb 330162562Sjhb /* Wait 60 ms between retries. */ 331167086Sjhb pause("retry", 60 * hz / 1000); 332162562Sjhb#ifdef SSIF_RETRY_DEBUG 333162562Sjhb device_printf(sc->ipmi_dev, 334162562Sjhb "SSIF: Retrying request (%d)\n", i + 1); 335162562Sjhb#endif 336162562Sjhb } 337162562Sjhb if (ok) 338162562Sjhb req->ir_error = 0; 339162562Sjhb else 340162562Sjhb req->ir_error = EIO; 341162562Sjhb IPMI_LOCK(sc); 342162562Sjhb ipmi_complete_request(sc, req); 343162562Sjhb IPMI_UNLOCK(sc); 344162562Sjhb 345162562Sjhb /* Enforce 10ms between requests. */ 346167086Sjhb pause("delay", hz / 100); 347162562Sjhb 348162562Sjhb IPMI_LOCK(sc); 349162562Sjhb } 350162562Sjhb IPMI_UNLOCK(sc); 351172836Sjulian kproc_exit(0); 352162562Sjhb} 353162562Sjhb 354162562Sjhbstatic int 355162562Sjhbssif_startup(struct ipmi_softc *sc) 356162562Sjhb{ 357162562Sjhb 358172836Sjulian return (kproc_create(ssif_loop, sc, &sc->ipmi_kthread, 0, 0, 359162562Sjhb "%s: ssif", device_get_nameunit(sc->ipmi_dev))); 360162562Sjhb} 361162562Sjhb 362278872Sscottlstatic int 363278872Sscottlssif_driver_request(struct ipmi_softc *sc, struct ipmi_request *req, int timo) 364278872Sscottl{ 365278872Sscottl int error; 366278872Sscottl 367278872Sscottl IPMI_LOCK(sc); 368278872Sscottl error = ipmi_polled_enqueue_request(sc, req); 369278872Sscottl if (error == 0) 370278872Sscottl error = msleep(req, &sc->ipmi_requests_lock, 0, "ipmireq", 371278872Sscottl timo); 372278872Sscottl if (error == 0) 373278872Sscottl error = req->ir_error; 374278872Sscottl IPMI_UNLOCK(sc); 375278872Sscottl return (error); 376278872Sscottl} 377278872Sscottl 378162562Sjhbint 379162562Sjhbipmi_ssif_attach(struct ipmi_softc *sc, device_t smbus, int smbus_address) 380162562Sjhb{ 381162562Sjhb 382162562Sjhb /* Setup smbus address. */ 383162562Sjhb sc->ipmi_ssif_smbus = smbus; 384162562Sjhb sc->ipmi_ssif_smbus_address = smbus_address; 385162562Sjhb 386162562Sjhb /* Setup function pointers. */ 387162562Sjhb sc->ipmi_startup = ssif_startup; 388162562Sjhb sc->ipmi_enqueue_request = ipmi_polled_enqueue_request; 389278872Sscottl sc->ipmi_driver_request = ssif_driver_request; 390162562Sjhb 391162562Sjhb return (0); 392162562Sjhb} 393