2 * Copyright (c) 2006 IronPort Systems Inc. <ambrisko@ironport.com>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * $FreeBSD: head/sys/dev/ipmi/ipmi_smic.c 248705 2013-03-25 14:30:34Z melifaro $
29 #include <sys/param.h>
30 #include <sys/systm.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>
45 #include <dev/misc/ipmi/ipmivars.h>
48 static void smic_wait_for_tx_okay(struct ipmi_softc *);
49 static void smic_wait_for_rx_okay(struct ipmi_softc *);
50 static void smic_wait_for_not_busy(struct ipmi_softc *);
51 static void smic_set_busy(struct ipmi_softc *);
54 smic_wait_for_tx_okay(struct ipmi_softc *sc)
59 flags = INB(sc, SMIC_FLAGS);
60 } while (!(flags & SMIC_STATUS_TX_RDY));
64 smic_wait_for_rx_okay(struct ipmi_softc *sc)
69 flags = INB(sc, SMIC_FLAGS);
70 } while (!(flags & SMIC_STATUS_RX_RDY));
74 smic_wait_for_not_busy(struct ipmi_softc *sc)
79 flags = INB(sc, SMIC_FLAGS);
80 } while (flags & SMIC_STATUS_BUSY);
84 smic_set_busy(struct ipmi_softc *sc)
88 flags = INB(sc, SMIC_FLAGS);
89 flags |= SMIC_STATUS_BUSY;
90 flags &= ~SMIC_STATUS_RESERVED;
91 OUTB(sc, SMIC_FLAGS, flags);
95 * Start a transfer with a WR_START transaction that sends the NetFn/LUN
99 smic_start_write(struct ipmi_softc *sc, u_char data)
101 u_char error, status;
103 smic_wait_for_not_busy(sc);
105 OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_WR_START);
106 OUTB(sc, SMIC_DATA, data);
108 smic_wait_for_not_busy(sc);
109 status = INB(sc, SMIC_CTL_STS);
110 if (status != SMIC_SC_SMS_WR_START) {
111 error = INB(sc, SMIC_DATA);
112 device_printf(sc->ipmi_dev, "SMIC: Write did not start %02x\n",
120 * Write a byte in the middle of the message (either the command or one of
121 * the data bytes) using a WR_NEXT transaction.
124 smic_write_next(struct ipmi_softc *sc, u_char data)
126 u_char error, status;
128 smic_wait_for_tx_okay(sc);
129 OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_WR_NEXT);
130 OUTB(sc, SMIC_DATA, data);
132 smic_wait_for_not_busy(sc);
133 status = INB(sc, SMIC_CTL_STS);
134 if (status != SMIC_SC_SMS_WR_NEXT) {
135 error = INB(sc, SMIC_DATA);
136 device_printf(sc->ipmi_dev, "SMIC: Write did not next %02x\n",
144 * Write the last byte of a transfer to end the write phase via a WR_END
148 smic_write_last(struct ipmi_softc *sc, u_char data)
150 u_char error, status;
152 smic_wait_for_tx_okay(sc);
153 OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_WR_END);
154 OUTB(sc, SMIC_DATA, data);
156 smic_wait_for_not_busy(sc);
157 status = INB(sc, SMIC_CTL_STS);
158 if (status != SMIC_SC_SMS_WR_END) {
159 error = INB(sc, SMIC_DATA);
160 device_printf(sc->ipmi_dev, "SMIC: Write did not end %02x\n",
168 * Start the read phase of a transfer with a RD_START transaction.
171 smic_start_read(struct ipmi_softc *sc, u_char *data)
173 u_char error, status;
175 smic_wait_for_not_busy(sc);
177 smic_wait_for_rx_okay(sc);
178 OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_RD_START);
180 smic_wait_for_not_busy(sc);
181 status = INB(sc, SMIC_CTL_STS);
182 if (status != SMIC_SC_SMS_RD_START) {
183 error = INB(sc, SMIC_DATA);
184 device_printf(sc->ipmi_dev, "SMIC: Read did not start %02x\n",
188 *data = INB(sc, SMIC_DATA);
193 * Read a byte via a RD_NEXT transaction. If this was the last byte, return
197 smic_read_byte(struct ipmi_softc *sc, u_char *data)
199 u_char error, status;
201 smic_wait_for_rx_okay(sc);
202 OUTB(sc, SMIC_CTL_STS, SMIC_SC_SMS_RD_NEXT);
204 smic_wait_for_not_busy(sc);
205 status = INB(sc, SMIC_CTL_STS);
206 if (status != SMIC_SC_SMS_RD_NEXT &&
207 status != SMIC_SC_SMS_RD_END) {
208 error = INB(sc, SMIC_DATA);
209 device_printf(sc->ipmi_dev, "SMIC: Read did not next %02x\n",
213 *data = INB(sc, SMIC_DATA);
214 if (status == SMIC_SC_SMS_RD_NEXT)
220 /* Complete a transfer via a RD_END transaction after reading the last byte. */
222 smic_read_end(struct ipmi_softc *sc)
224 u_char error, status;
226 OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_RD_END);
228 smic_wait_for_not_busy(sc);
229 status = INB(sc, SMIC_CTL_STS);
230 if (status != SMIC_SC_SMS_RDY) {
231 error = INB(sc, SMIC_DATA);
232 device_printf(sc->ipmi_dev, "SMIC: Read did not end %02x\n",
240 smic_polled_request(struct ipmi_softc *sc, struct ipmi_request *req)
245 /* First, start the message with the address. */
246 if (!smic_start_write(sc, req->ir_addr))
249 device_printf(sc->ipmi_dev, "SMIC: WRITE_START address: %02x\n",
253 if (req->ir_requestlen == 0) {
254 /* Send the command as the last byte. */
255 if (!smic_write_last(sc, req->ir_command))
258 device_printf(sc->ipmi_dev, "SMIC: Wrote command: %02x\n",
262 /* Send the command. */
263 if (!smic_write_next(sc, req->ir_command))
266 device_printf(sc->ipmi_dev, "SMIC: Wrote command: %02x\n",
270 /* Send the payload. */
271 cp = req->ir_request;
272 for (i = 0; i < req->ir_requestlen - 1; i++) {
273 if (!smic_write_next(sc, *cp++))
276 device_printf(sc->ipmi_dev, "SMIC: Wrote data: %02x\n",
280 if (!smic_write_last(sc, *cp))
283 device_printf(sc->ipmi_dev, "SMIC: Write last data: %02x\n",
288 /* Start the read phase by reading the NetFn/LUN. */
289 if (smic_start_read(sc, &data) != 1)
292 device_printf(sc->ipmi_dev, "SMIC: Read address: %02x\n", data);
294 if (data != IPMI_REPLY_ADDR(req->ir_addr)) {
295 device_printf(sc->ipmi_dev, "SMIC: Reply address mismatch\n");
299 /* Read the command. */
300 if (smic_read_byte(sc, &data) != 1)
303 device_printf(sc->ipmi_dev, "SMIC: Read command: %02x\n", data);
305 if (data != req->ir_command) {
306 device_printf(sc->ipmi_dev, "SMIC: Command mismatch\n");
310 /* Read the completion code. */
311 state = smic_read_byte(sc, &req->ir_compcode);
315 device_printf(sc->ipmi_dev, "SMIC: Read completion code: %02x\n",
319 /* Finally, read the reply from the BMC. */
322 state = smic_read_byte(sc, &data);
325 if (i < req->ir_replybuflen) {
326 req->ir_reply[i] = data;
328 device_printf(sc->ipmi_dev, "SMIC: Read data: %02x\n",
331 device_printf(sc->ipmi_dev,
332 "SMIC: Read short %02x byte %d\n", data, i + 1);
338 /* Terminate the transfer. */
339 if (!smic_read_end(sc))
341 req->ir_replylen = i;
343 device_printf(sc->ipmi_dev, "SMIC: READ finished (%d bytes)\n", i);
344 if (req->ir_replybuflen < i)
346 if (req->ir_replybuflen < i && req->ir_replybuflen != 0)
348 device_printf(sc->ipmi_dev,
349 "SMIC: Read short: %zd buffer, %d actual\n",
350 req->ir_replybuflen, i);
357 struct ipmi_softc *sc = arg;
358 struct ipmi_request *req;
362 while ((req = ipmi_dequeue_request(sc)) != NULL) {
365 for (i = 0; i < 3 && !ok; i++)
366 ok = smic_polled_request(sc, req);
372 ipmi_complete_request(sc, req);
379 smic_startup(struct ipmi_softc *sc)
382 return (kthread_create(smic_loop, sc, &sc->ipmi_kthread,
383 "%s: smic", device_get_nameunit(sc->ipmi_dev)));
387 ipmi_smic_attach(struct ipmi_softc *sc)
391 /* Setup function pointers. */
392 sc->ipmi_startup = smic_startup;
393 sc->ipmi_enqueue_request = ipmi_polled_enqueue_request;
395 /* See if we can talk to the controller. */
396 flags = INB(sc, SMIC_FLAGS);
398 device_printf(sc->ipmi_dev, "couldn't find it\n");
403 device_printf(sc->ipmi_dev, "SMIC: initial state: %02x\n", flags);