1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 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 __FBSDID("$FreeBSD$"); 31 32 #include <sys/param.h> 33 #include <sys/systm.h> 34 #include <sys/bus.h> 35 #include <sys/condvar.h> 36 #include <sys/eventhandler.h> 37 #include <sys/kernel.h> 38 #include <sys/kthread.h> 39 #include <sys/module.h> 40 #include <sys/selinfo.h> 41 42 #include <dev/smbus/smbconf.h> 43 #include <dev/smbus/smb.h> 44 45 #include "smbus_if.h" 46 47 #ifdef LOCAL_MODULE 48 #include <ipmivars.h> 49 #else 50 #include <dev/ipmi/ipmivars.h> 51 #endif 52 53 #define SMBUS_WRITE_SINGLE 0x02 54 #define SMBUS_WRITE_START 0x06 55 #define SMBUS_WRITE_CONT 0x07 56 #define SMBUS_READ_START 0x03 57 #define SMBUS_READ_CONT 0x09 58 #define SMBUS_DATA_SIZE 32 59 60 #ifdef SSIF_DEBUG 61 static void 62 dump_buffer(device_t dev, const char *msg, u_char *bytes, int len) 63 { 64 int i; 65 66 device_printf(dev, "%s:", msg); 67 for (i = 0; i < len; i++) 68 printf(" %02x", bytes[i]); 69 printf("\n"); 70 } 71 #endif 72 73 static int 74 ssif_polled_request(struct ipmi_softc *sc, struct ipmi_request *req) 75 { 76 u_char ssif_buf[SMBUS_DATA_SIZE]; 77 device_t dev = sc->ipmi_dev; 78 device_t smbus = sc->ipmi_ssif_smbus; 79 u_char *cp, block, count, offset; 80 size_t len; 81 int error; 82 83 /* Acquire the bus while we send the request. */ 84 if (smbus_request_bus(smbus, dev, SMB_WAIT) != 0) 85 return (0); 86 87 /* 88 * First, send out the request. Begin by filling out the first 89 * packet which includes the NetFn/LUN and command. 90 */ 91 ssif_buf[0] = req->ir_addr; 92 ssif_buf[1] = req->ir_command; 93 if (req->ir_requestlen > 0) 94 bcopy(req->ir_request, &ssif_buf[2], 95 min(req->ir_requestlen, SMBUS_DATA_SIZE - 2)); 96 97 /* Small requests are sent with a single command. */ 98 if (req->ir_requestlen <= 30) { 99 #ifdef SSIF_DEBUG 100 dump_buffer(dev, "WRITE_SINGLE", ssif_buf, 101 req->ir_requestlen + 2); 102 #endif 103 error = smbus_error(smbus_bwrite(smbus, 104 sc->ipmi_ssif_smbus_address, SMBUS_WRITE_SINGLE, 105 req->ir_requestlen + 2, ssif_buf)); 106 if (error) { 107 #ifdef SSIF_ERROR_DEBUG 108 device_printf(dev, "SSIF: WRITE_SINGLE error %d\n", 109 error); 110 #endif 111 goto fail; 112 } 113 } else { 114 /* Longer requests are sent out in 32-byte messages. */ 115 #ifdef SSIF_DEBUG 116 dump_buffer(dev, "WRITE_START", ssif_buf, SMBUS_DATA_SIZE); 117 #endif 118 error = smbus_error(smbus_bwrite(smbus, 119 sc->ipmi_ssif_smbus_address, SMBUS_WRITE_START, 120 SMBUS_DATA_SIZE, ssif_buf)); 121 if (error) { 122 #ifdef SSIF_ERROR_DEBUG 123 device_printf(dev, "SSIF: WRITE_START error %d\n", 124 error); 125 #endif 126 goto fail; 127 } 128 129 len = req->ir_requestlen - (SMBUS_DATA_SIZE - 2); 130 cp = req->ir_request + (SMBUS_DATA_SIZE - 2); 131 while (len > 0) { 132 #ifdef SSIF_DEBUG 133 dump_buffer(dev, "WRITE_CONT", cp, 134 min(len, SMBUS_DATA_SIZE)); 135 #endif 136 error = smbus_error(smbus_bwrite(smbus, 137 sc->ipmi_ssif_smbus_address, SMBUS_WRITE_CONT, 138 min(len, SMBUS_DATA_SIZE), cp)); 139 if (error) { 140 #ifdef SSIF_ERROR_DEBUG 141 device_printf(dev, "SSIF: WRITE_CONT error %d\n", 142 error); 143 #endif 144 goto fail; 145 } 146 cp += SMBUS_DATA_SIZE; 147 len -= SMBUS_DATA_SIZE; 148 } 149 150 /* 151 * The final WRITE_CONT transaction has to have a non-zero 152 * length that is also not SMBUS_DATA_SIZE. If our last 153 * WRITE_CONT transaction in the loop sent SMBUS_DATA_SIZE 154 * bytes, then len will be 0, and we send an extra 0x00 byte 155 * to terminate the transaction. 156 */ 157 if (len == 0) { 158 char c = 0; 159 160 #ifdef SSIF_DEBUG 161 dump_buffer(dev, "WRITE_CONT", &c, 1); 162 #endif 163 error = smbus_error(smbus_bwrite(smbus, 164 sc->ipmi_ssif_smbus_address, SMBUS_WRITE_CONT, 165 1, &c)); 166 if (error) { 167 #ifdef SSIF_ERROR_DEBUG 168 device_printf(dev, "SSIF: WRITE_CONT error %d\n", 169 error); 170 #endif 171 goto fail; 172 } 173 } 174 } 175 176 /* Release the bus. */ 177 smbus_release_bus(smbus, dev); 178 179 /* Give the BMC 100ms to chew on the request. */ 180 pause("ssifwt", hz / 10); 181 182 /* Try to read the first packet. */ 183 read_start: 184 if (smbus_request_bus(smbus, dev, SMB_WAIT) != 0) 185 return (0); 186 count = SMBUS_DATA_SIZE; 187 error = smbus_error(smbus_bread(smbus, 188 sc->ipmi_ssif_smbus_address, SMBUS_READ_START, &count, ssif_buf)); 189 if (error == ENXIO || error == EBUSY) { 190 smbus_release_bus(smbus, dev); 191 #ifdef SSIF_DEBUG 192 device_printf(dev, "SSIF: READ_START retry\n"); 193 #endif 194 /* Give the BMC another 10ms. */ 195 pause("ssifwt", hz / 100); 196 goto read_start; 197 } 198 if (error) { 199 #ifdef SSIF_ERROR_DEBUG 200 device_printf(dev, "SSIF: READ_START failed: %d\n", error); 201 #endif 202 goto fail; 203 } 204 #ifdef SSIF_DEBUG 205 device_printf("SSIF: READ_START: ok\n"); 206 #endif 207 208 /* 209 * If this is the first part of a multi-part read, then we need to 210 * skip the first two bytes. 211 */ 212 if (count == SMBUS_DATA_SIZE && ssif_buf[0] == 0 && ssif_buf[1] == 1) 213 offset = 2; 214 else 215 offset = 0; 216 217 /* We had better get the reply header. */ 218 if (count < 3) { 219 device_printf(dev, "SSIF: Short reply packet\n"); 220 goto fail; 221 } 222 223 /* Verify the NetFn/LUN. */ 224 if (ssif_buf[offset] != IPMI_REPLY_ADDR(req->ir_addr)) { 225 device_printf(dev, "SSIF: Reply address mismatch\n"); 226 goto fail; 227 } 228 229 /* Verify the command. */ 230 if (ssif_buf[offset + 1] != req->ir_command) { 231 device_printf(dev, "SMIC: Command mismatch\n"); 232 goto fail; 233 } 234 235 /* Read the completion code. */ 236 req->ir_compcode = ssif_buf[offset + 2]; 237 238 /* If this is a single read, just copy the data and return. */ 239 if (offset == 0) { 240 #ifdef SSIF_DEBUG 241 dump_buffer(dev, "READ_SINGLE", ssif_buf, count); 242 #endif 243 len = count - 3; 244 bcopy(&ssif_buf[3], req->ir_reply, 245 min(req->ir_replybuflen, len)); 246 goto done; 247 } 248 249 /* 250 * This is the first part of a multi-read transaction, so copy 251 * out the payload and start looping. 252 */ 253 #ifdef SSIF_DEBUG 254 dump_buffer(dev, "READ_START", ssif_buf + 2, count - 2); 255 #endif 256 bcopy(&ssif_buf[5], req->ir_reply, min(req->ir_replybuflen, count - 5)); 257 len = count - 5; 258 block = 1; 259 260 for (;;) { 261 /* Read another packet via READ_CONT. */ 262 count = SMBUS_DATA_SIZE; 263 error = smbus_error(smbus_bread(smbus, 264 sc->ipmi_ssif_smbus_address, SMBUS_READ_CONT, &count, 265 ssif_buf)); 266 if (error) { 267 #ifdef SSIF_ERROR_DEBUG 268 printf("SSIF: READ_CONT failed: %d\n", error); 269 #endif 270 goto fail; 271 } 272 #ifdef SSIF_DEBUG 273 device_printf(dev, "SSIF: READ_CONT... ok\n"); 274 #endif 275 276 /* Verify the block number. 0xff marks the last block. */ 277 if (ssif_buf[0] != 0xff && ssif_buf[0] != block) { 278 device_printf(dev, "SSIF: Read wrong block %d %d\n", 279 ssif_buf[0], block); 280 goto fail; 281 } 282 if (ssif_buf[0] != 0xff && count < SMBUS_DATA_SIZE) { 283 device_printf(dev, 284 "SSIF: Read short middle block, length %d\n", 285 count); 286 goto fail; 287 } 288 #ifdef SSIF_DEBUG 289 if (ssif_buf[0] == 0xff) 290 dump_buffer(dev, "READ_END", ssif_buf + 1, count - 1); 291 else 292 dump_buffer(dev, "READ_CONT", ssif_buf + 1, count - 1); 293 #endif 294 if (len < req->ir_replybuflen) 295 bcopy(&ssif_buf[1], &req->ir_reply[len], 296 min(req->ir_replybuflen - len, count - 1)); 297 len += count - 1; 298 299 /* If this was the last block we are done. */ 300 if (ssif_buf[0] == 0xff) 301 break; 302 block++; 303 } 304 305 done: 306 /* Save the total length and return success. */ 307 req->ir_replylen = len; 308 smbus_release_bus(smbus, dev); 309 return (1); 310 311 fail: 312 smbus_release_bus(smbus, dev); 313 return (0); 314 } 315 316 static void 317 ssif_loop(void *arg) 318 { 319 struct ipmi_softc *sc = arg; 320 struct ipmi_request *req; 321 int i, ok; 322 323 IPMI_LOCK(sc); 324 while ((req = ipmi_dequeue_request(sc)) != NULL) { 325 IPMI_UNLOCK(sc); 326 ok = 0; 327 for (i = 0; i < 5; i++) { 328 ok = ssif_polled_request(sc, req); 329 if (ok) 330 break; 331 332 /* Wait 60 ms between retries. */ 333 pause("retry", 60 * hz / 1000); 334 #ifdef SSIF_RETRY_DEBUG 335 device_printf(sc->ipmi_dev, 336 "SSIF: Retrying request (%d)\n", i + 1); 337 #endif 338 } 339 if (ok) 340 req->ir_error = 0; 341 else 342 req->ir_error = EIO; 343 IPMI_LOCK(sc); 344 ipmi_complete_request(sc, req); 345 IPMI_UNLOCK(sc); 346 347 /* Enforce 10ms between requests. */ 348 pause("delay", hz / 100); 349 350 IPMI_LOCK(sc); 351 } 352 IPMI_UNLOCK(sc); 353 kproc_exit(0); 354 } 355 356 static int 357 ssif_startup(struct ipmi_softc *sc) 358 { 359 360 return (kproc_create(ssif_loop, sc, &sc->ipmi_kthread, 0, 0, 361 "%s: ssif", device_get_nameunit(sc->ipmi_dev))); 362 } 363 364 static int 365 ssif_driver_request(struct ipmi_softc *sc, struct ipmi_request *req, int timo) 366 { 367 int error; 368 369 IPMI_LOCK(sc); 370 error = ipmi_polled_enqueue_request(sc, req); 371 if (error == 0) 372 error = msleep(req, &sc->ipmi_requests_lock, 0, "ipmireq", 373 timo); 374 if (error == 0) 375 error = req->ir_error; 376 IPMI_UNLOCK(sc); 377 return (error); 378 } 379 380 int 381 ipmi_ssif_attach(struct ipmi_softc *sc, device_t smbus, int smbus_address) 382 { 383 384 /* Setup smbus address. */ 385 sc->ipmi_ssif_smbus = smbus; 386 sc->ipmi_ssif_smbus_address = smbus_address; 387 388 /* Setup function pointers. */ 389 sc->ipmi_startup = ssif_startup; 390 sc->ipmi_enqueue_request = ipmi_polled_enqueue_request; 391 sc->ipmi_driver_request = ssif_driver_request; 392 393 return (0); 394 } 395