xref: /freebsd/sys/dev/ipmi/ipmi_smic.c (revision b0d29bc47dba79f6f38e67eabadfb4b32ffd9390)
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 }
435