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