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