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
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
30 #include <sys/param.h>
31 #include <sys/systm.h>
33 #include <sys/condvar.h>
34 #include <sys/eventhandler.h>
35 #include <sys/kernel.h>
36 #include <sys/kthread.h>
37 #include <sys/module.h>
39 #include <sys/selinfo.h>
40 #include <machine/bus.h>
47 #include <dev/ipmi/ipmivars.h>
50 static void smic_wait_for_tx_okay(struct ipmi_softc *);
51 static void smic_wait_for_rx_okay(struct ipmi_softc *);
52 static void smic_wait_for_not_busy(struct ipmi_softc *);
53 static void smic_set_busy(struct ipmi_softc *);
56 smic_wait_for_tx_okay(struct ipmi_softc *sc)
61 flags = INB(sc, SMIC_FLAGS);
62 } while (!(flags & SMIC_STATUS_TX_RDY));
66 smic_wait_for_rx_okay(struct ipmi_softc *sc)
71 flags = INB(sc, SMIC_FLAGS);
72 } while (!(flags & SMIC_STATUS_RX_RDY));
76 smic_wait_for_not_busy(struct ipmi_softc *sc)
81 flags = INB(sc, SMIC_FLAGS);
82 } while (flags & SMIC_STATUS_BUSY);
86 smic_set_busy(struct ipmi_softc *sc)
90 flags = INB(sc, SMIC_FLAGS);
91 flags |= SMIC_STATUS_BUSY;
92 flags &= ~SMIC_STATUS_RESERVED;
93 OUTB(sc, SMIC_FLAGS, flags);
97 * Start a transfer with a WR_START transaction that sends the NetFn/LUN
101 smic_start_write(struct ipmi_softc *sc, u_char data)
103 u_char error, status;
105 smic_wait_for_not_busy(sc);
107 OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_WR_START);
108 OUTB(sc, SMIC_DATA, data);
110 smic_wait_for_not_busy(sc);
111 status = INB(sc, SMIC_CTL_STS);
112 if (status != SMIC_SC_SMS_WR_START) {
113 error = INB(sc, SMIC_DATA);
114 device_printf(sc->ipmi_dev, "SMIC: Write did not start %02x\n",
122 * Write a byte in the middle of the message (either the command or one of
123 * the data bytes) using a WR_NEXT transaction.
126 smic_write_next(struct ipmi_softc *sc, u_char data)
128 u_char error, status;
130 smic_wait_for_tx_okay(sc);
131 OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_WR_NEXT);
132 OUTB(sc, SMIC_DATA, data);
134 smic_wait_for_not_busy(sc);
135 status = INB(sc, SMIC_CTL_STS);
136 if (status != SMIC_SC_SMS_WR_NEXT) {
137 error = INB(sc, SMIC_DATA);
138 device_printf(sc->ipmi_dev, "SMIC: Write did not next %02x\n",
146 * Write the last byte of a transfer to end the write phase via a WR_END
150 smic_write_last(struct ipmi_softc *sc, u_char data)
152 u_char error, status;
154 smic_wait_for_tx_okay(sc);
155 OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_WR_END);
156 OUTB(sc, SMIC_DATA, data);
158 smic_wait_for_not_busy(sc);
159 status = INB(sc, SMIC_CTL_STS);
160 if (status != SMIC_SC_SMS_WR_END) {
161 error = INB(sc, SMIC_DATA);
162 device_printf(sc->ipmi_dev, "SMIC: Write did not end %02x\n",
170 * Start the read phase of a transfer with a RD_START transaction.
173 smic_start_read(struct ipmi_softc *sc, u_char *data)
175 u_char error, status;
177 smic_wait_for_not_busy(sc);
179 smic_wait_for_rx_okay(sc);
180 OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_RD_START);
182 smic_wait_for_not_busy(sc);
183 status = INB(sc, SMIC_CTL_STS);
184 if (status != SMIC_SC_SMS_RD_START) {
185 error = INB(sc, SMIC_DATA);
186 device_printf(sc->ipmi_dev, "SMIC: Read did not start %02x\n",
190 *data = INB(sc, SMIC_DATA);
195 * Read a byte via a RD_NEXT transaction. If this was the last byte, return
199 smic_read_byte(struct ipmi_softc *sc, u_char *data)
201 u_char error, status;
203 smic_wait_for_rx_okay(sc);
204 OUTB(sc, SMIC_CTL_STS, SMIC_SC_SMS_RD_NEXT);
206 smic_wait_for_not_busy(sc);
207 status = INB(sc, SMIC_CTL_STS);
208 if (status != SMIC_SC_SMS_RD_NEXT &&
209 status != SMIC_SC_SMS_RD_END) {
210 error = INB(sc, SMIC_DATA);
211 device_printf(sc->ipmi_dev, "SMIC: Read did not next %02x\n",
215 *data = INB(sc, SMIC_DATA);
216 if (status == SMIC_SC_SMS_RD_NEXT)
222 /* Complete a transfer via a RD_END transaction after reading the last byte. */
224 smic_read_end(struct ipmi_softc *sc)
226 u_char error, status;
228 OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_RD_END);
230 smic_wait_for_not_busy(sc);
231 status = INB(sc, SMIC_CTL_STS);
232 if (status != SMIC_SC_SMS_RDY) {
233 error = INB(sc, SMIC_DATA);
234 device_printf(sc->ipmi_dev, "SMIC: Read did not end %02x\n",
242 smic_polled_request(struct ipmi_softc *sc, struct ipmi_request *req)
247 /* First, start the message with the address. */
248 if (!smic_start_write(sc, req->ir_addr))
251 device_printf(sc->ipmi_dev, "SMIC: WRITE_START address: %02x\n",
255 if (req->ir_requestlen == 0) {
256 /* Send the command as the last byte. */
257 if (!smic_write_last(sc, req->ir_command))
260 device_printf(sc->ipmi_dev, "SMIC: Wrote command: %02x\n",
264 /* Send the command. */
265 if (!smic_write_next(sc, req->ir_command))
268 device_printf(sc->ipmi_dev, "SMIC: Wrote command: %02x\n",
272 /* Send the payload. */
273 cp = req->ir_request;
274 for (i = 0; i < req->ir_requestlen - 1; i++) {
275 if (!smic_write_next(sc, *cp++))
278 device_printf(sc->ipmi_dev, "SMIC: Wrote data: %02x\n",
282 if (!smic_write_last(sc, *cp))
285 device_printf(sc->ipmi_dev, "SMIC: Write last data: %02x\n",
290 /* Start the read phase by reading the NetFn/LUN. */
291 if (smic_start_read(sc, &data) != 1)
294 device_printf(sc->ipmi_dev, "SMIC: Read address: %02x\n", data);
296 if (data != IPMI_REPLY_ADDR(req->ir_addr)) {
297 device_printf(sc->ipmi_dev, "SMIC: Reply address mismatch\n");
301 /* Read the command. */
302 if (smic_read_byte(sc, &data) != 1)
305 device_printf(sc->ipmi_dev, "SMIC: Read command: %02x\n", data);
307 if (data != req->ir_command) {
308 device_printf(sc->ipmi_dev, "SMIC: Command mismatch\n");
312 /* Read the completion code. */
313 state = smic_read_byte(sc, &req->ir_compcode);
317 device_printf(sc->ipmi_dev, "SMIC: Read completion code: %02x\n",
321 /* Finally, read the reply from the BMC. */
324 state = smic_read_byte(sc, &data);
327 if (i < req->ir_replybuflen) {
328 req->ir_reply[i] = data;
330 device_printf(sc->ipmi_dev, "SMIC: Read data: %02x\n",
333 device_printf(sc->ipmi_dev,
334 "SMIC: Read short %02x byte %d\n", data, i + 1);
340 /* Terminate the transfer. */
341 if (!smic_read_end(sc))
343 req->ir_replylen = i;
345 device_printf(sc->ipmi_dev, "SMIC: READ finished (%d bytes)\n", i);
346 if (req->ir_replybuflen < i)
348 if (req->ir_replybuflen < i && req->ir_replybuflen != 0)
350 device_printf(sc->ipmi_dev,
351 "SMIC: Read short: %zd buffer, %d actual\n",
352 req->ir_replybuflen, i);
359 struct ipmi_softc *sc = arg;
360 struct ipmi_request *req;
364 while ((req = ipmi_dequeue_request(sc)) != NULL) {
367 for (i = 0; i < 3 && !ok; i++)
368 ok = smic_polled_request(sc, req);
374 ipmi_complete_request(sc, req);
381 smic_startup(struct ipmi_softc *sc)
384 return (kproc_create(smic_loop, sc, &sc->ipmi_kthread, 0, 0,
385 "%s: smic", device_get_nameunit(sc->ipmi_dev)));
389 ipmi_smic_attach(struct ipmi_softc *sc)
393 /* Setup function pointers. */
394 sc->ipmi_startup = smic_startup;
395 sc->ipmi_enqueue_request = ipmi_polled_enqueue_request;
397 /* See if we can talk to the controller. */
398 flags = INB(sc, SMIC_FLAGS);
400 device_printf(sc->ipmi_dev, "couldn't find it\n");
405 device_printf(sc->ipmi_dev, "SMIC: initial state: %02x\n", flags);