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>
41 #include <sys/selinfo.h>
42 #include <machine/bus.h>
49 #include <dev/ipmi/ipmivars.h>
52 static void smic_wait_for_tx_okay(struct ipmi_softc *);
53 static void smic_wait_for_rx_okay(struct ipmi_softc *);
54 static void smic_wait_for_not_busy(struct ipmi_softc *);
55 static void smic_set_busy(struct ipmi_softc *);
58 smic_wait_for_tx_okay(struct ipmi_softc *sc)
63 flags = INB(sc, SMIC_FLAGS);
64 } while (!(flags & SMIC_STATUS_TX_RDY));
68 smic_wait_for_rx_okay(struct ipmi_softc *sc)
73 flags = INB(sc, SMIC_FLAGS);
74 } while (!(flags & SMIC_STATUS_RX_RDY));
78 smic_wait_for_not_busy(struct ipmi_softc *sc)
83 flags = INB(sc, SMIC_FLAGS);
84 } while (flags & SMIC_STATUS_BUSY);
88 smic_set_busy(struct ipmi_softc *sc)
92 flags = INB(sc, SMIC_FLAGS);
93 flags |= SMIC_STATUS_BUSY;
94 flags &= ~SMIC_STATUS_RESERVED;
95 OUTB(sc, SMIC_FLAGS, flags);
99 * Start a transfer with a WR_START transaction that sends the NetFn/LUN
103 smic_start_write(struct ipmi_softc *sc, u_char data)
105 u_char error, status;
107 smic_wait_for_not_busy(sc);
109 OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_WR_START);
110 OUTB(sc, SMIC_DATA, data);
112 smic_wait_for_not_busy(sc);
113 status = INB(sc, SMIC_CTL_STS);
114 if (status != SMIC_SC_SMS_WR_START) {
115 error = INB(sc, SMIC_DATA);
116 device_printf(sc->ipmi_dev, "SMIC: Write did not start %02x\n",
124 * Write a byte in the middle of the message (either the command or one of
125 * the data bytes) using a WR_NEXT transaction.
128 smic_write_next(struct ipmi_softc *sc, u_char data)
130 u_char error, status;
132 smic_wait_for_tx_okay(sc);
133 OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_WR_NEXT);
134 OUTB(sc, SMIC_DATA, data);
136 smic_wait_for_not_busy(sc);
137 status = INB(sc, SMIC_CTL_STS);
138 if (status != SMIC_SC_SMS_WR_NEXT) {
139 error = INB(sc, SMIC_DATA);
140 device_printf(sc->ipmi_dev, "SMIC: Write did not next %02x\n",
148 * Write the last byte of a transfer to end the write phase via a WR_END
152 smic_write_last(struct ipmi_softc *sc, u_char data)
154 u_char error, status;
156 smic_wait_for_tx_okay(sc);
157 OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_WR_END);
158 OUTB(sc, SMIC_DATA, data);
160 smic_wait_for_not_busy(sc);
161 status = INB(sc, SMIC_CTL_STS);
162 if (status != SMIC_SC_SMS_WR_END) {
163 error = INB(sc, SMIC_DATA);
164 device_printf(sc->ipmi_dev, "SMIC: Write did not end %02x\n",
172 * Start the read phase of a transfer with a RD_START transaction.
175 smic_start_read(struct ipmi_softc *sc, u_char *data)
177 u_char error, status;
179 smic_wait_for_not_busy(sc);
181 smic_wait_for_rx_okay(sc);
182 OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_RD_START);
184 smic_wait_for_not_busy(sc);
185 status = INB(sc, SMIC_CTL_STS);
186 if (status != SMIC_SC_SMS_RD_START) {
187 error = INB(sc, SMIC_DATA);
188 device_printf(sc->ipmi_dev, "SMIC: Read did not start %02x\n",
192 *data = INB(sc, SMIC_DATA);
197 * Read a byte via a RD_NEXT transaction. If this was the last byte, return
201 smic_read_byte(struct ipmi_softc *sc, u_char *data)
203 u_char error, status;
205 smic_wait_for_rx_okay(sc);
206 OUTB(sc, SMIC_CTL_STS, SMIC_SC_SMS_RD_NEXT);
208 smic_wait_for_not_busy(sc);
209 status = INB(sc, SMIC_CTL_STS);
210 if (status != SMIC_SC_SMS_RD_NEXT &&
211 status != SMIC_SC_SMS_RD_END) {
212 error = INB(sc, SMIC_DATA);
213 device_printf(sc->ipmi_dev, "SMIC: Read did not next %02x\n",
217 *data = INB(sc, SMIC_DATA);
218 if (status == SMIC_SC_SMS_RD_NEXT)
224 /* Complete a transfer via a RD_END transaction after reading the last byte. */
226 smic_read_end(struct ipmi_softc *sc)
228 u_char error, status;
230 OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_RD_END);
232 smic_wait_for_not_busy(sc);
233 status = INB(sc, SMIC_CTL_STS);
234 if (status != SMIC_SC_SMS_RDY) {
235 error = INB(sc, SMIC_DATA);
236 device_printf(sc->ipmi_dev, "SMIC: Read did not end %02x\n",
244 smic_polled_request(struct ipmi_softc *sc, struct ipmi_request *req)
249 /* First, start the message with the address. */
250 if (!smic_start_write(sc, req->ir_addr))
253 device_printf(sc->ipmi_dev, "SMIC: WRITE_START address: %02x\n",
257 if (req->ir_requestlen == 0) {
258 /* Send the command as the last byte. */
259 if (!smic_write_last(sc, req->ir_command))
262 device_printf(sc->ipmi_dev, "SMIC: Wrote command: %02x\n",
266 /* Send the command. */
267 if (!smic_write_next(sc, req->ir_command))
270 device_printf(sc->ipmi_dev, "SMIC: Wrote command: %02x\n",
274 /* Send the payload. */
275 cp = req->ir_request;
276 for (i = 0; i < req->ir_requestlen - 1; i++) {
277 if (!smic_write_next(sc, *cp++))
280 device_printf(sc->ipmi_dev, "SMIC: Wrote data: %02x\n",
284 if (!smic_write_last(sc, *cp))
287 device_printf(sc->ipmi_dev, "SMIC: Write last data: %02x\n",
292 /* Start the read phase by reading the NetFn/LUN. */
293 if (smic_start_read(sc, &data) != 1)
296 device_printf(sc->ipmi_dev, "SMIC: Read address: %02x\n", data);
298 if (data != IPMI_REPLY_ADDR(req->ir_addr)) {
299 device_printf(sc->ipmi_dev, "SMIC: Reply address mismatch\n");
303 /* Read the command. */
304 if (smic_read_byte(sc, &data) != 1)
307 device_printf(sc->ipmi_dev, "SMIC: Read command: %02x\n", data);
309 if (data != req->ir_command) {
310 device_printf(sc->ipmi_dev, "SMIC: Command mismatch\n");
314 /* Read the completion code. */
315 state = smic_read_byte(sc, &req->ir_compcode);
319 device_printf(sc->ipmi_dev, "SMIC: Read completion code: %02x\n",
323 /* Finally, read the reply from the BMC. */
326 state = smic_read_byte(sc, &data);
329 if (i < req->ir_replybuflen) {
330 req->ir_reply[i] = data;
332 device_printf(sc->ipmi_dev, "SMIC: Read data: %02x\n",
335 device_printf(sc->ipmi_dev,
336 "SMIC: Read short %02x byte %d\n", data, i + 1);
342 /* Terminate the transfer. */
343 if (!smic_read_end(sc))
345 req->ir_replylen = i;
347 device_printf(sc->ipmi_dev, "SMIC: READ finished (%d bytes)\n", i);
348 if (req->ir_replybuflen < i)
350 if (req->ir_replybuflen < i && req->ir_replybuflen != 0)
352 device_printf(sc->ipmi_dev,
353 "SMIC: Read short: %zd buffer, %d actual\n",
354 req->ir_replybuflen, i);
361 struct ipmi_softc *sc = arg;
362 struct ipmi_request *req;
366 while ((req = ipmi_dequeue_request(sc)) != NULL) {
369 for (i = 0; i < 3 && !ok; i++) {
371 ok = smic_polled_request(sc, req);
379 ipmi_complete_request(sc, req);
386 smic_startup(struct ipmi_softc *sc)
389 return (kproc_create(smic_loop, sc, &sc->ipmi_kthread, 0, 0,
390 "%s: smic", device_get_nameunit(sc->ipmi_dev)));
394 smic_driver_request(struct ipmi_softc *sc, struct ipmi_request *req, int timo)
399 for (i = 0; i < 3 && !ok; i++) {
401 ok = smic_polled_request(sc, req);
408 return (req->ir_error);
412 ipmi_smic_attach(struct ipmi_softc *sc)
416 /* Setup function pointers. */
417 sc->ipmi_startup = smic_startup;
418 sc->ipmi_enqueue_request = ipmi_polled_enqueue_request;
419 sc->ipmi_driver_request = smic_driver_request;
420 sc->ipmi_driver_requests_polled = 1;
422 /* See if we can talk to the controller. */
423 flags = INB(sc, SMIC_FLAGS);
425 device_printf(sc->ipmi_dev, "couldn't find it\n");
430 device_printf(sc->ipmi_dev, "SMIC: initial state: %02x\n", flags);