2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 2006 IronPort Systems Inc. <ambrisko@ironport.com>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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.
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
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
32 #include <sys/param.h>
33 #include <sys/systm.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>
42 #include <dev/smbus/smbconf.h>
43 #include <dev/smbus/smb.h>
50 #include <dev/ipmi/ipmivars.h>
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
62 dump_buffer(device_t dev, const char *msg, u_char *bytes, int len)
66 device_printf(dev, "%s:", msg);
67 for (i = 0; i < len; i++)
68 printf(" %02x", bytes[i]);
74 ssif_polled_request(struct ipmi_softc *sc, struct ipmi_request *req)
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;
83 /* Acquire the bus while we send the request. */
84 if (smbus_request_bus(smbus, dev, SMB_WAIT) != 0)
88 * First, send out the request. Begin by filling out the first
89 * packet which includes the NetFn/LUN and command.
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));
97 /* Small requests are sent with a single command. */
98 if (req->ir_requestlen <= 30) {
100 dump_buffer(dev, "WRITE_SINGLE", ssif_buf,
101 req->ir_requestlen + 2);
103 error = smbus_error(smbus_bwrite(smbus,
104 sc->ipmi_ssif_smbus_address, SMBUS_WRITE_SINGLE,
105 req->ir_requestlen + 2, ssif_buf));
107 #ifdef SSIF_ERROR_DEBUG
108 device_printf(dev, "SSIF: WRITE_SINGLE error %d\n",
114 /* Longer requests are sent out in 32-byte messages. */
116 dump_buffer(dev, "WRITE_START", ssif_buf, SMBUS_DATA_SIZE);
118 error = smbus_error(smbus_bwrite(smbus,
119 sc->ipmi_ssif_smbus_address, SMBUS_WRITE_START,
120 SMBUS_DATA_SIZE, ssif_buf));
122 #ifdef SSIF_ERROR_DEBUG
123 device_printf(dev, "SSIF: WRITE_START error %d\n",
129 len = req->ir_requestlen - (SMBUS_DATA_SIZE - 2);
130 cp = req->ir_request + (SMBUS_DATA_SIZE - 2);
133 dump_buffer(dev, "WRITE_CONT", cp,
134 min(len, SMBUS_DATA_SIZE));
136 error = smbus_error(smbus_bwrite(smbus,
137 sc->ipmi_ssif_smbus_address, SMBUS_WRITE_CONT,
138 min(len, SMBUS_DATA_SIZE), cp));
140 #ifdef SSIF_ERROR_DEBUG
141 device_printf(dev, "SSIF: WRITE_CONT error %d\n",
146 cp += SMBUS_DATA_SIZE;
147 len -= SMBUS_DATA_SIZE;
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.
161 dump_buffer(dev, "WRITE_CONT", &c, 1);
163 error = smbus_error(smbus_bwrite(smbus,
164 sc->ipmi_ssif_smbus_address, SMBUS_WRITE_CONT,
167 #ifdef SSIF_ERROR_DEBUG
168 device_printf(dev, "SSIF: WRITE_CONT error %d\n",
176 /* Release the bus. */
177 smbus_release_bus(smbus, dev);
179 /* Give the BMC 100ms to chew on the request. */
180 pause("ssifwt", hz / 10);
182 /* Try to read the first packet. */
184 if (smbus_request_bus(smbus, dev, SMB_WAIT) != 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);
192 device_printf(dev, "SSIF: READ_START retry\n");
194 /* Give the BMC another 10ms. */
195 pause("ssifwt", hz / 100);
199 #ifdef SSIF_ERROR_DEBUG
200 device_printf(dev, "SSIF: READ_START failed: %d\n", error);
205 device_printf("SSIF: READ_START: ok\n");
209 * If this is the first part of a multi-part read, then we need to
210 * skip the first two bytes.
212 if (count == SMBUS_DATA_SIZE && ssif_buf[0] == 0 && ssif_buf[1] == 1)
217 /* We had better get the reply header. */
219 device_printf(dev, "SSIF: Short reply packet\n");
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");
229 /* Verify the command. */
230 if (ssif_buf[offset + 1] != req->ir_command) {
231 device_printf(dev, "SMIC: Command mismatch\n");
235 /* Read the completion code. */
236 req->ir_compcode = ssif_buf[offset + 2];
238 /* If this is a single read, just copy the data and return. */
241 dump_buffer(dev, "READ_SINGLE", ssif_buf, count);
244 bcopy(&ssif_buf[3], req->ir_reply,
245 min(req->ir_replybuflen, len));
250 * This is the first part of a multi-read transaction, so copy
251 * out the payload and start looping.
254 dump_buffer(dev, "READ_START", ssif_buf + 2, count - 2);
256 bcopy(&ssif_buf[5], req->ir_reply, min(req->ir_replybuflen, count - 5));
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,
267 #ifdef SSIF_ERROR_DEBUG
268 printf("SSIF: READ_CONT failed: %d\n", error);
273 device_printf(dev, "SSIF: READ_CONT... ok\n");
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",
282 if (ssif_buf[0] != 0xff && count < SMBUS_DATA_SIZE) {
284 "SSIF: Read short middle block, length %d\n",
289 if (ssif_buf[0] == 0xff)
290 dump_buffer(dev, "READ_END", ssif_buf + 1, count - 1);
292 dump_buffer(dev, "READ_CONT", ssif_buf + 1, count - 1);
294 if (len < req->ir_replybuflen)
295 bcopy(&ssif_buf[1], &req->ir_reply[len],
296 min(req->ir_replybuflen - len, count - 1));
299 /* If this was the last block we are done. */
300 if (ssif_buf[0] != 0xff)
306 /* Save the total length and return success. */
307 req->ir_replylen = len;
308 smbus_release_bus(smbus, dev);
312 smbus_release_bus(smbus, dev);
319 struct ipmi_softc *sc = arg;
320 struct ipmi_request *req;
324 while ((req = ipmi_dequeue_request(sc)) != NULL) {
327 for (i = 0; i < 5; i++) {
328 ok = ssif_polled_request(sc, req);
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);
344 ipmi_complete_request(sc, req);
347 /* Enforce 10ms between requests. */
348 pause("delay", hz / 100);
357 ssif_startup(struct ipmi_softc *sc)
360 return (kproc_create(ssif_loop, sc, &sc->ipmi_kthread, 0, 0,
361 "%s: ssif", device_get_nameunit(sc->ipmi_dev)));
365 ssif_driver_request(struct ipmi_softc *sc, struct ipmi_request *req, int timo)
370 error = ipmi_polled_enqueue_request(sc, req);
372 error = msleep(req, &sc->ipmi_requests_lock, 0, "ipmireq",
375 error = req->ir_error;
381 ipmi_ssif_attach(struct ipmi_softc *sc, device_t smbus, int smbus_address)
384 /* Setup smbus address. */
385 sc->ipmi_ssif_smbus = smbus;
386 sc->ipmi_ssif_smbus_address = smbus_address;
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;