1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2006 IronPort Systems Inc. <ambrisko@ironport.com>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
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.
15 *
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
26 * SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/bus.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/rman.h>
41 #include <sys/selinfo.h>
42 #include <machine/bus.h>
43
44 #ifdef LOCAL_MODULE
45 #include <ipmi.h>
46 #include <ipmivars.h>
47 #else
48 #include <sys/ipmi.h>
49 #include <dev/ipmi/ipmivars.h>
50 #endif
51
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 *);
56
57 static void
58 smic_wait_for_tx_okay(struct ipmi_softc *sc)
59 {
60 int flags;
61
62 do {
63 flags = INB(sc, SMIC_FLAGS);
64 } while (!(flags & SMIC_STATUS_TX_RDY));
65 }
66
67 static void
68 smic_wait_for_rx_okay(struct ipmi_softc *sc)
69 {
70 int flags;
71
72 do {
73 flags = INB(sc, SMIC_FLAGS);
74 } while (!(flags & SMIC_STATUS_RX_RDY));
75 }
76
77 static void
78 smic_wait_for_not_busy(struct ipmi_softc *sc)
79 {
80 int flags;
81
82 do {
83 flags = INB(sc, SMIC_FLAGS);
84 } while (flags & SMIC_STATUS_BUSY);
85 }
86
87 static void
88 smic_set_busy(struct ipmi_softc *sc)
89 {
90 int flags;
91
92 flags = INB(sc, SMIC_FLAGS);
93 flags |= SMIC_STATUS_BUSY;
94 flags &= ~SMIC_STATUS_RESERVED;
95 OUTB(sc, SMIC_FLAGS, flags);
96 }
97
98 /*
99 * Start a transfer with a WR_START transaction that sends the NetFn/LUN
100 * address.
101 */
102 static int
103 smic_start_write(struct ipmi_softc *sc, u_char data)
104 {
105 u_char error, status;
106
107 smic_wait_for_not_busy(sc);
108
109 OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_WR_START);
110 OUTB(sc, SMIC_DATA, data);
111 smic_set_busy(sc);
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",
117 error);
118 return (0);
119 }
120 return (1);
121 }
122
123 /*
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.
126 */
127 static int
128 smic_write_next(struct ipmi_softc *sc, u_char data)
129 {
130 u_char error, status;
131
132 smic_wait_for_tx_okay(sc);
133 OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_WR_NEXT);
134 OUTB(sc, SMIC_DATA, data);
135 smic_set_busy(sc);
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",
141 error);
142 return (0);
143 }
144 return (1);
145 }
146
147 /*
148 * Write the last byte of a transfer to end the write phase via a WR_END
149 * transaction.
150 */
151 static int
152 smic_write_last(struct ipmi_softc *sc, u_char data)
153 {
154 u_char error, status;
155
156 smic_wait_for_tx_okay(sc);
157 OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_WR_END);
158 OUTB(sc, SMIC_DATA, data);
159 smic_set_busy(sc);
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",
165 error);
166 return (0);
167 }
168 return (1);
169 }
170
171 /*
172 * Start the read phase of a transfer with a RD_START transaction.
173 */
174 static int
175 smic_start_read(struct ipmi_softc *sc, u_char *data)
176 {
177 u_char error, status;
178
179 smic_wait_for_not_busy(sc);
180
181 smic_wait_for_rx_okay(sc);
182 OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_RD_START);
183 smic_set_busy(sc);
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",
189 error);
190 return (0);
191 }
192 *data = INB(sc, SMIC_DATA);
193 return (1);
194 }
195
196 /*
197 * Read a byte via a RD_NEXT transaction. If this was the last byte, return
198 * 2 rather than 1.
199 */
200 static int
201 smic_read_byte(struct ipmi_softc *sc, u_char *data)
202 {
203 u_char error, status;
204
205 smic_wait_for_rx_okay(sc);
206 OUTB(sc, SMIC_CTL_STS, SMIC_SC_SMS_RD_NEXT);
207 smic_set_busy(sc);
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",
214 error);
215 return (0);
216 }
217 *data = INB(sc, SMIC_DATA);
218 if (status == SMIC_SC_SMS_RD_NEXT)
219 return (1);
220 else
221 return (2);
222 }
223
224 /* Complete a transfer via a RD_END transaction after reading the last byte. */
225 static int
226 smic_read_end(struct ipmi_softc *sc)
227 {
228 u_char error, status;
229
230 OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_RD_END);
231 smic_set_busy(sc);
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",
237 error);
238 return (0);
239 }
240 return (1);
241 }
242
243 static int
244 smic_polled_request(struct ipmi_softc *sc, struct ipmi_request *req)
245 {
246 u_char *cp, data;
247 int i, state;
248
249 /* First, start the message with the address. */
250 if (!smic_start_write(sc, req->ir_addr))
251 return (0);
252 #ifdef SMIC_DEBUG
253 device_printf(sc->ipmi_dev, "SMIC: WRITE_START address: %02x\n",
254 req->ir_addr);
255 #endif
256
257 if (req->ir_requestlen == 0) {
258 /* Send the command as the last byte. */
259 if (!smic_write_last(sc, req->ir_command))
260 return (0);
261 #ifdef SMIC_DEBUG
262 device_printf(sc->ipmi_dev, "SMIC: Wrote command: %02x\n",
263 req->ir_command);
264 #endif
265 } else {
266 /* Send the command. */
267 if (!smic_write_next(sc, req->ir_command))
268 return (0);
269 #ifdef SMIC_DEBUG
270 device_printf(sc->ipmi_dev, "SMIC: Wrote command: %02x\n",
271 req->ir_command);
272 #endif
273
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++))
278 return (0);
279 #ifdef SMIC_DEBUG
280 device_printf(sc->ipmi_dev, "SMIC: Wrote data: %02x\n",
281 cp[-1]);
282 #endif
283 }
284 if (!smic_write_last(sc, *cp))
285 return (0);
286 #ifdef SMIC_DEBUG
287 device_printf(sc->ipmi_dev, "SMIC: Write last data: %02x\n",
288 *cp);
289 #endif
290 }
291
292 /* Start the read phase by reading the NetFn/LUN. */
293 if (smic_start_read(sc, &data) != 1)
294 return (0);
295 #ifdef SMIC_DEBUG
296 device_printf(sc->ipmi_dev, "SMIC: Read address: %02x\n", data);
297 #endif
298 if (data != IPMI_REPLY_ADDR(req->ir_addr)) {
299 device_printf(sc->ipmi_dev, "SMIC: Reply address mismatch\n");
300 return (0);
301 }
302
303 /* Read the command. */
304 if (smic_read_byte(sc, &data) != 1)
305 return (0);
306 #ifdef SMIC_DEBUG
307 device_printf(sc->ipmi_dev, "SMIC: Read command: %02x\n", data);
308 #endif
309 if (data != req->ir_command) {
310 device_printf(sc->ipmi_dev, "SMIC: Command mismatch\n");
311 return (0);
312 }
313
314 /* Read the completion code. */
315 state = smic_read_byte(sc, &req->ir_compcode);
316 if (state == 0)
317 return (0);
318 #ifdef SMIC_DEBUG
319 device_printf(sc->ipmi_dev, "SMIC: Read completion code: %02x\n",
320 req->ir_compcode);
321 #endif
322
323 /* Finally, read the reply from the BMC. */
324 i = 0;
325 while (state == 1) {
326 state = smic_read_byte(sc, &data);
327 if (state == 0)
328 return (0);
329 if (i < req->ir_replybuflen) {
330 req->ir_reply[i] = data;
331 #ifdef SMIC_DEBUG
332 device_printf(sc->ipmi_dev, "SMIC: Read data: %02x\n",
333 data);
334 } else {
335 device_printf(sc->ipmi_dev,
336 "SMIC: Read short %02x byte %d\n", data, i + 1);
337 #endif
338 }
339 i++;
340 }
341
342 /* Terminate the transfer. */
343 if (!smic_read_end(sc))
344 return (0);
345 req->ir_replylen = i;
346 #ifdef SMIC_DEBUG
347 device_printf(sc->ipmi_dev, "SMIC: READ finished (%d bytes)\n", i);
348 if (req->ir_replybuflen < i)
349 #else
350 if (req->ir_replybuflen < i && req->ir_replybuflen != 0)
351 #endif
352 device_printf(sc->ipmi_dev,
353 "SMIC: Read short: %zd buffer, %d actual\n",
354 req->ir_replybuflen, i);
355 return (1);
356 }
357
358 static void
359 smic_loop(void *arg)
360 {
361 struct ipmi_softc *sc = arg;
362 struct ipmi_request *req;
363 int i, ok;
364
365 IPMI_LOCK(sc);
366 while ((req = ipmi_dequeue_request(sc)) != NULL) {
367 IPMI_UNLOCK(sc);
368 ok = 0;
369 for (i = 0; i < 3 && !ok; i++) {
370 IPMI_IO_LOCK(sc);
371 ok = smic_polled_request(sc, req);
372 IPMI_IO_UNLOCK(sc);
373 }
374 if (ok)
375 req->ir_error = 0;
376 else
377 req->ir_error = EIO;
378 IPMI_LOCK(sc);
379 ipmi_complete_request(sc, req);
380 }
381 IPMI_UNLOCK(sc);
382 kproc_exit(0);
383 }
384
385 static int
386 smic_startup(struct ipmi_softc *sc)
387 {
388
389 return (kproc_create(smic_loop, sc, &sc->ipmi_kthread, 0, 0,
390 "%s: smic", device_get_nameunit(sc->ipmi_dev)));
391 }
392
393 static int
394 smic_driver_request(struct ipmi_softc *sc, struct ipmi_request *req, int timo)
395 {
396 int i, ok;
397
398 ok = 0;
399 for (i = 0; i < 3 && !ok; i++) {
400 IPMI_IO_LOCK(sc);
401 ok = smic_polled_request(sc, req);
402 IPMI_IO_UNLOCK(sc);
403 }
404 if (ok)
405 req->ir_error = 0;
406 else
407 req->ir_error = EIO;
408 return (req->ir_error);
409 }
410
411 int
412 ipmi_smic_attach(struct ipmi_softc *sc)
413 {
414 int flags;
415
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;
421
422 /* See if we can talk to the controller. */
423 flags = INB(sc, SMIC_FLAGS);
424 if (flags == 0xff) {
425 device_printf(sc->ipmi_dev, "couldn't find it\n");
426 return (ENXIO);
427 }
428
429 #ifdef SMIC_DEBUG
430 device_printf(sc->ipmi_dev, "SMIC: initial state: %02x\n", flags);
431 #endif
432
433 return (0);
434 }
Cache object: f9df0c8c017fa0f2402a4651b1a620d3
|