1 /*-
2 * Copyright (c) 2006 IronPort Systems Inc. <ambrisko@ironport.com>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
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.
13 *
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
24 * SUCH DAMAGE.
25 *
26 * $FreeBSD: head/sys/dev/ipmi/ipmi_smic.c 248705 2013-03-25 14:30:34Z melifaro $
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/rman.h>
38 #include <sys/conf.h>
39
40 #ifdef LOCAL_MODULE
41 #include <ipmi.h>
42 #include <ipmivars.h>
43 #else
44 #include <sys/ipmi.h>
45 #include <dev/misc/ipmi/ipmivars.h>
46 #endif
47
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 *);
52
53 static void
smic_wait_for_tx_okay(struct ipmi_softc * sc)54 smic_wait_for_tx_okay(struct ipmi_softc *sc)
55 {
56 int flags;
57
58 do {
59 flags = INB(sc, SMIC_FLAGS);
60 } while (!(flags & SMIC_STATUS_TX_RDY));
61 }
62
63 static void
smic_wait_for_rx_okay(struct ipmi_softc * sc)64 smic_wait_for_rx_okay(struct ipmi_softc *sc)
65 {
66 int flags;
67
68 do {
69 flags = INB(sc, SMIC_FLAGS);
70 } while (!(flags & SMIC_STATUS_RX_RDY));
71 }
72
73 static void
smic_wait_for_not_busy(struct ipmi_softc * sc)74 smic_wait_for_not_busy(struct ipmi_softc *sc)
75 {
76 int flags;
77
78 do {
79 flags = INB(sc, SMIC_FLAGS);
80 } while (flags & SMIC_STATUS_BUSY);
81 }
82
83 static void
smic_set_busy(struct ipmi_softc * sc)84 smic_set_busy(struct ipmi_softc *sc)
85 {
86 int flags;
87
88 flags = INB(sc, SMIC_FLAGS);
89 flags |= SMIC_STATUS_BUSY;
90 flags &= ~SMIC_STATUS_RESERVED;
91 OUTB(sc, SMIC_FLAGS, flags);
92 }
93
94 /*
95 * Start a transfer with a WR_START transaction that sends the NetFn/LUN
96 * address.
97 */
98 static int
smic_start_write(struct ipmi_softc * sc,u_char data)99 smic_start_write(struct ipmi_softc *sc, u_char data)
100 {
101 u_char error, status;
102
103 smic_wait_for_not_busy(sc);
104
105 OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_WR_START);
106 OUTB(sc, SMIC_DATA, data);
107 smic_set_busy(sc);
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",
113 error);
114 return (0);
115 }
116 return (1);
117 }
118
119 /*
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.
122 */
123 static int
smic_write_next(struct ipmi_softc * sc,u_char data)124 smic_write_next(struct ipmi_softc *sc, u_char data)
125 {
126 u_char error, status;
127
128 smic_wait_for_tx_okay(sc);
129 OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_WR_NEXT);
130 OUTB(sc, SMIC_DATA, data);
131 smic_set_busy(sc);
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",
137 error);
138 return (0);
139 }
140 return (1);
141 }
142
143 /*
144 * Write the last byte of a transfer to end the write phase via a WR_END
145 * transaction.
146 */
147 static int
smic_write_last(struct ipmi_softc * sc,u_char data)148 smic_write_last(struct ipmi_softc *sc, u_char data)
149 {
150 u_char error, status;
151
152 smic_wait_for_tx_okay(sc);
153 OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_WR_END);
154 OUTB(sc, SMIC_DATA, data);
155 smic_set_busy(sc);
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",
161 error);
162 return (0);
163 }
164 return (1);
165 }
166
167 /*
168 * Start the read phase of a transfer with a RD_START transaction.
169 */
170 static int
smic_start_read(struct ipmi_softc * sc,u_char * data)171 smic_start_read(struct ipmi_softc *sc, u_char *data)
172 {
173 u_char error, status;
174
175 smic_wait_for_not_busy(sc);
176
177 smic_wait_for_rx_okay(sc);
178 OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_RD_START);
179 smic_set_busy(sc);
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",
185 error);
186 return (0);
187 }
188 *data = INB(sc, SMIC_DATA);
189 return (1);
190 }
191
192 /*
193 * Read a byte via a RD_NEXT transaction. If this was the last byte, return
194 * 2 rather than 1.
195 */
196 static int
smic_read_byte(struct ipmi_softc * sc,u_char * data)197 smic_read_byte(struct ipmi_softc *sc, u_char *data)
198 {
199 u_char error, status;
200
201 smic_wait_for_rx_okay(sc);
202 OUTB(sc, SMIC_CTL_STS, SMIC_SC_SMS_RD_NEXT);
203 smic_set_busy(sc);
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",
210 error);
211 return (0);
212 }
213 *data = INB(sc, SMIC_DATA);
214 if (status == SMIC_SC_SMS_RD_NEXT)
215 return (1);
216 else
217 return (2);
218 }
219
220 /* Complete a transfer via a RD_END transaction after reading the last byte. */
221 static int
smic_read_end(struct ipmi_softc * sc)222 smic_read_end(struct ipmi_softc *sc)
223 {
224 u_char error, status;
225
226 OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_RD_END);
227 smic_set_busy(sc);
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",
233 error);
234 return (0);
235 }
236 return (1);
237 }
238
239 static int
smic_polled_request(struct ipmi_softc * sc,struct ipmi_request * req)240 smic_polled_request(struct ipmi_softc *sc, struct ipmi_request *req)
241 {
242 u_char *cp, data;
243 int i, state;
244
245 /* First, start the message with the address. */
246 if (!smic_start_write(sc, req->ir_addr))
247 return (0);
248 #ifdef SMIC_DEBUG
249 device_printf(sc->ipmi_dev, "SMIC: WRITE_START address: %02x\n",
250 req->ir_addr);
251 #endif
252
253 if (req->ir_requestlen == 0) {
254 /* Send the command as the last byte. */
255 if (!smic_write_last(sc, req->ir_command))
256 return (0);
257 #ifdef SMIC_DEBUG
258 device_printf(sc->ipmi_dev, "SMIC: Wrote command: %02x\n",
259 req->ir_command);
260 #endif
261 } else {
262 /* Send the command. */
263 if (!smic_write_next(sc, req->ir_command))
264 return (0);
265 #ifdef SMIC_DEBUG
266 device_printf(sc->ipmi_dev, "SMIC: Wrote command: %02x\n",
267 req->ir_command);
268 #endif
269
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++))
274 return (0);
275 #ifdef SMIC_DEBUG
276 device_printf(sc->ipmi_dev, "SMIC: Wrote data: %02x\n",
277 cp[-1]);
278 #endif
279 }
280 if (!smic_write_last(sc, *cp))
281 return (0);
282 #ifdef SMIC_DEBUG
283 device_printf(sc->ipmi_dev, "SMIC: Write last data: %02x\n",
284 *cp);
285 #endif
286 }
287
288 /* Start the read phase by reading the NetFn/LUN. */
289 if (smic_start_read(sc, &data) != 1)
290 return (0);
291 #ifdef SMIC_DEBUG
292 device_printf(sc->ipmi_dev, "SMIC: Read address: %02x\n", data);
293 #endif
294 if (data != IPMI_REPLY_ADDR(req->ir_addr)) {
295 device_printf(sc->ipmi_dev, "SMIC: Reply address mismatch\n");
296 return (0);
297 }
298
299 /* Read the command. */
300 if (smic_read_byte(sc, &data) != 1)
301 return (0);
302 #ifdef SMIC_DEBUG
303 device_printf(sc->ipmi_dev, "SMIC: Read command: %02x\n", data);
304 #endif
305 if (data != req->ir_command) {
306 device_printf(sc->ipmi_dev, "SMIC: Command mismatch\n");
307 return (0);
308 }
309
310 /* Read the completion code. */
311 state = smic_read_byte(sc, &req->ir_compcode);
312 if (state == 0)
313 return (0);
314 #ifdef SMIC_DEBUG
315 device_printf(sc->ipmi_dev, "SMIC: Read completion code: %02x\n",
316 req->ir_compcode);
317 #endif
318
319 /* Finally, read the reply from the BMC. */
320 i = 0;
321 while (state == 1) {
322 state = smic_read_byte(sc, &data);
323 if (state == 0)
324 return (0);
325 if (i < req->ir_replybuflen) {
326 req->ir_reply[i] = data;
327 #ifdef SMIC_DEBUG
328 device_printf(sc->ipmi_dev, "SMIC: Read data: %02x\n",
329 data);
330 } else {
331 device_printf(sc->ipmi_dev,
332 "SMIC: Read short %02x byte %d\n", data, i + 1);
333 #endif
334 }
335 i++;
336 }
337
338 /* Terminate the transfer. */
339 if (!smic_read_end(sc))
340 return (0);
341 req->ir_replylen = i;
342 #ifdef SMIC_DEBUG
343 device_printf(sc->ipmi_dev, "SMIC: READ finished (%d bytes)\n", i);
344 if (req->ir_replybuflen < i)
345 #else
346 if (req->ir_replybuflen < i && req->ir_replybuflen != 0)
347 #endif
348 device_printf(sc->ipmi_dev,
349 "SMIC: Read short: %zd buffer, %d actual\n",
350 req->ir_replybuflen, i);
351 return (1);
352 }
353
354 static void
smic_loop(void * arg)355 smic_loop(void *arg)
356 {
357 struct ipmi_softc *sc = arg;
358 struct ipmi_request *req;
359 int i, ok;
360
361 IPMI_LOCK(sc);
362 while ((req = ipmi_dequeue_request(sc)) != NULL) {
363 IPMI_UNLOCK(sc);
364 ok = 0;
365 for (i = 0; i < 3 && !ok; i++)
366 ok = smic_polled_request(sc, req);
367 if (ok)
368 req->ir_error = 0;
369 else
370 req->ir_error = EIO;
371 IPMI_LOCK(sc);
372 ipmi_complete_request(sc, req);
373 }
374 IPMI_UNLOCK(sc);
375 kthread_exit();
376 }
377
378 static int
smic_startup(struct ipmi_softc * sc)379 smic_startup(struct ipmi_softc *sc)
380 {
381
382 return (kthread_create(smic_loop, sc, &sc->ipmi_kthread,
383 "%s: smic", device_get_nameunit(sc->ipmi_dev)));
384 }
385
386 int
ipmi_smic_attach(struct ipmi_softc * sc)387 ipmi_smic_attach(struct ipmi_softc *sc)
388 {
389 int flags;
390
391 /* Setup function pointers. */
392 sc->ipmi_startup = smic_startup;
393 sc->ipmi_enqueue_request = ipmi_polled_enqueue_request;
394
395 /* See if we can talk to the controller. */
396 flags = INB(sc, SMIC_FLAGS);
397 if (flags == 0xff) {
398 device_printf(sc->ipmi_dev, "couldn't find it\n");
399 return (ENXIO);
400 }
401
402 #ifdef SMIC_DEBUG
403 device_printf(sc->ipmi_dev, "SMIC: initial state: %02x\n", flags);
404 #endif
405
406 return (0);
407 }
408