xref: /freebsd/sys/dev/ipmi/ipmi_smic.c (revision e0c4386e7e71d93b0edc0c8fa156263fc4a8b0b6)
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
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
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
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
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
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
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
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
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
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
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
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
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
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
391 smic_driver_request(struct ipmi_softc *sc, struct ipmi_request *req, int timo)
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
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