1162562Sjhb/*-
2162562Sjhb * Copyright (c) 2006 IronPort Systems Inc. <ambrisko@ironport.com>
3162562Sjhb * All rights reserved.
4162562Sjhb *
5162562Sjhb * Redistribution and use in source and binary forms, with or without
6162562Sjhb * modification, are permitted provided that the following conditions
7162562Sjhb * are met:
8162562Sjhb * 1. Redistributions of source code must retain the above copyright
9162562Sjhb *    notice, this list of conditions and the following disclaimer.
10162562Sjhb * 2. Redistributions in binary form must reproduce the above copyright
11162562Sjhb *    notice, this list of conditions and the following disclaimer in the
12162562Sjhb *    documentation and/or other materials provided with the distribution.
13162562Sjhb *
14162562Sjhb * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15162562Sjhb * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16162562Sjhb * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17162562Sjhb * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18162562Sjhb * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19162562Sjhb * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20162562Sjhb * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21162562Sjhb * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22162562Sjhb * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23162562Sjhb * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24162562Sjhb * SUCH DAMAGE.
25162562Sjhb */
26162562Sjhb
27162562Sjhb#include <sys/cdefs.h>
28162562Sjhb__FBSDID("$FreeBSD$");
29162562Sjhb
30162562Sjhb#include <sys/param.h>
31162562Sjhb#include <sys/systm.h>
32162562Sjhb#include <sys/bus.h>
33162562Sjhb#include <sys/condvar.h>
34162562Sjhb#include <sys/eventhandler.h>
35162562Sjhb#include <sys/kernel.h>
36162562Sjhb#include <sys/kthread.h>
37162562Sjhb#include <sys/rman.h>
38162562Sjhb#include <sys/selinfo.h>
39162562Sjhb#include <machine/bus.h>
40162562Sjhb
41162562Sjhb#ifdef LOCAL_MODULE
42162562Sjhb#include <ipmi.h>
43162562Sjhb#include <ipmivars.h>
44162562Sjhb#else
45162562Sjhb#include <sys/ipmi.h>
46162562Sjhb#include <dev/ipmi/ipmivars.h>
47162562Sjhb#endif
48162562Sjhb
49162562Sjhbstatic void	kcs_clear_obf(struct ipmi_softc *, int);
50162562Sjhbstatic void	kcs_error(struct ipmi_softc *);
51162562Sjhbstatic int	kcs_wait_for_ibf(struct ipmi_softc *, int);
52162562Sjhbstatic int	kcs_wait_for_obf(struct ipmi_softc *, int);
53162562Sjhb
54162562Sjhbstatic int
55162562Sjhbkcs_wait_for_ibf(struct ipmi_softc *sc, int state)
56162562Sjhb{
57162562Sjhb	int status, start = ticks;
58162562Sjhb
59162562Sjhb	status = INB(sc, KCS_CTL_STS);
60162562Sjhb	if (state == 0) {
61162562Sjhb		/* WAIT FOR IBF = 0 */
62162562Sjhb		while (ticks - start < MAX_TIMEOUT && status & KCS_STATUS_IBF) {
63162562Sjhb			DELAY(100);
64162562Sjhb			status = INB(sc, KCS_CTL_STS);
65162562Sjhb		}
66162562Sjhb	} else {
67162562Sjhb		/* WAIT FOR IBF = 1 */
68162562Sjhb		while (ticks - start < MAX_TIMEOUT &&
69162562Sjhb		    !(status & KCS_STATUS_IBF)) {
70162562Sjhb			DELAY(100);
71162562Sjhb			status = INB(sc, KCS_CTL_STS);
72162562Sjhb		}
73162562Sjhb	}
74162562Sjhb	return (status);
75162562Sjhb}
76162562Sjhb
77162562Sjhbstatic int
78162562Sjhbkcs_wait_for_obf(struct ipmi_softc *sc, int state)
79162562Sjhb{
80162562Sjhb	int status, start = ticks;
81162562Sjhb
82162562Sjhb	status = INB(sc, KCS_CTL_STS);
83162562Sjhb	if (state == 0) {
84162562Sjhb		/* WAIT FOR OBF = 0 */
85162562Sjhb		while (ticks - start < MAX_TIMEOUT && status & KCS_STATUS_OBF) {
86162562Sjhb			DELAY(100);
87162562Sjhb			status = INB(sc, KCS_CTL_STS);
88162562Sjhb		}
89162562Sjhb	} else {
90162562Sjhb		/* WAIT FOR OBF = 1 */
91162562Sjhb		while (ticks - start < MAX_TIMEOUT &&
92162562Sjhb		    !(status & KCS_STATUS_OBF)) {
93162562Sjhb			DELAY(100);
94162562Sjhb			status = INB(sc, KCS_CTL_STS);
95162562Sjhb		}
96162562Sjhb	}
97162562Sjhb	return (status);
98162562Sjhb}
99162562Sjhb
100162562Sjhbstatic void
101162562Sjhbkcs_clear_obf(struct ipmi_softc *sc, int status)
102162562Sjhb{
103162562Sjhb	int data;
104162562Sjhb
105162562Sjhb	/* Clear OBF */
106162562Sjhb	if (status & KCS_STATUS_OBF) {
107162562Sjhb		data = INB(sc, KCS_DATA);
108162562Sjhb	}
109162562Sjhb}
110162562Sjhb
111162562Sjhbstatic void
112162562Sjhbkcs_error(struct ipmi_softc *sc)
113162562Sjhb{
114162562Sjhb	int retry, status;
115162562Sjhb	u_char data;
116162562Sjhb
117162562Sjhb	for (retry = 0; retry < 2; retry++) {
118162562Sjhb
119162562Sjhb		/* Wait for IBF = 0 */
120162562Sjhb		status = kcs_wait_for_ibf(sc, 0);
121162562Sjhb
122162562Sjhb		/* ABORT */
123162562Sjhb		OUTB(sc, KCS_CTL_STS, KCS_CONTROL_GET_STATUS_ABORT);
124162562Sjhb
125162562Sjhb		/* Wait for IBF = 0 */
126162562Sjhb		status = kcs_wait_for_ibf(sc, 0);
127162562Sjhb
128162562Sjhb		/* Clear OBF */
129162562Sjhb		kcs_clear_obf(sc, status);
130162562Sjhb
131162562Sjhb		if (status & KCS_STATUS_OBF) {
132162562Sjhb			data = INB(sc, KCS_DATA);
133162562Sjhb			if (data != 0)
134162562Sjhb				device_printf(sc->ipmi_dev,
135162562Sjhb				    "KCS Error Data %02x\n", data);
136162562Sjhb		}
137162562Sjhb
138162562Sjhb		/* 0x00 to DATA_IN */
139162562Sjhb		OUTB(sc, KCS_DATA, 0x00);
140162562Sjhb
141162562Sjhb		/* Wait for IBF = 0 */
142162562Sjhb		status = kcs_wait_for_ibf(sc, 0);
143162562Sjhb
144162562Sjhb		if (KCS_STATUS_STATE(status) == KCS_STATUS_STATE_READ) {
145162562Sjhb
146162562Sjhb			/* Wait for OBF = 1 */
147162562Sjhb			status = kcs_wait_for_obf(sc, 1);
148162562Sjhb
149162562Sjhb			/* Read error status */
150162562Sjhb			data = INB(sc, KCS_DATA);
151162562Sjhb			if (data != 0)
152162562Sjhb				device_printf(sc->ipmi_dev, "KCS error: %02x\n",
153162562Sjhb				    data);
154162562Sjhb
155162562Sjhb			/* Write READ into Data_in */
156162562Sjhb			OUTB(sc, KCS_DATA, KCS_DATA_IN_READ);
157162562Sjhb
158162562Sjhb			/* Wait for IBF = 0 */
159162562Sjhb			status = kcs_wait_for_ibf(sc, 0);
160162562Sjhb		}
161162562Sjhb
162162562Sjhb		/* IDLE STATE */
163162562Sjhb		if (KCS_STATUS_STATE(status) == KCS_STATUS_STATE_IDLE) {
164162562Sjhb			/* Wait for OBF = 1 */
165162562Sjhb			status = kcs_wait_for_obf(sc, 1);
166162562Sjhb
167162562Sjhb			/* Clear OBF */
168162562Sjhb			kcs_clear_obf(sc, status);
169162562Sjhb			return;
170162562Sjhb		}
171162562Sjhb	}
172182321Sjhb	device_printf(sc->ipmi_dev, "KCS: Error retry exhausted\n");
173162562Sjhb}
174162562Sjhb
175162562Sjhb/*
176162562Sjhb * Start to write a request.  Waits for IBF to clear and then sends the
177162562Sjhb * WR_START command.
178162562Sjhb */
179162562Sjhbstatic int
180162562Sjhbkcs_start_write(struct ipmi_softc *sc)
181162562Sjhb{
182162562Sjhb	int retry, status;
183162562Sjhb
184162562Sjhb	for (retry = 0; retry < 10; retry++) {
185162562Sjhb		/* Wait for IBF = 0 */
186162562Sjhb		status = kcs_wait_for_ibf(sc, 0);
187162562Sjhb
188162562Sjhb		/* Clear OBF */
189162562Sjhb		kcs_clear_obf(sc, status);
190162562Sjhb
191162562Sjhb		/* Write start to command */
192162562Sjhb		OUTB(sc, KCS_CTL_STS, KCS_CONTROL_WRITE_START);
193162562Sjhb
194162562Sjhb		/* Wait for IBF = 0 */
195162562Sjhb		status = kcs_wait_for_ibf(sc, 0);
196162562Sjhb		if (KCS_STATUS_STATE(status) == KCS_STATUS_STATE_WRITE)
197162562Sjhb			break;
198162562Sjhb		DELAY(1000000);
199162562Sjhb	}
200162562Sjhb
201162562Sjhb	if (KCS_STATUS_STATE(status) != KCS_STATUS_STATE_WRITE)
202162562Sjhb		/* error state */
203162562Sjhb		return (0);
204162562Sjhb
205162562Sjhb	/* Clear OBF */
206162562Sjhb	kcs_clear_obf(sc, status);
207162562Sjhb
208162562Sjhb	return (1);
209162562Sjhb}
210162562Sjhb
211162562Sjhb/*
212162562Sjhb * Write a byte of the request message, excluding the last byte of the
213162562Sjhb * message which requires special handling.
214162562Sjhb */
215162562Sjhbstatic int
216162562Sjhbkcs_write_byte(struct ipmi_softc *sc, u_char data)
217162562Sjhb{
218162562Sjhb	int status;
219162562Sjhb
220162562Sjhb	/* Data to Data */
221162562Sjhb	OUTB(sc, KCS_DATA, data);
222162562Sjhb
223162562Sjhb	/* Wait for IBF = 0 */
224162562Sjhb	status = kcs_wait_for_ibf(sc, 0);
225162562Sjhb
226162562Sjhb	if (KCS_STATUS_STATE(status) != KCS_STATUS_STATE_WRITE)
227162562Sjhb		return (0);
228162562Sjhb
229162562Sjhb	/* Clear OBF */
230162562Sjhb	kcs_clear_obf(sc, status);
231162562Sjhb	return (1);
232162562Sjhb}
233162562Sjhb
234162562Sjhb/*
235162562Sjhb * Write the last byte of a request message.
236162562Sjhb */
237162562Sjhbstatic int
238162562Sjhbkcs_write_last_byte(struct ipmi_softc *sc, u_char data)
239162562Sjhb{
240162562Sjhb	int status;
241162562Sjhb
242162562Sjhb	/* Write end to command */
243162562Sjhb	OUTB(sc, KCS_CTL_STS, KCS_CONTROL_WRITE_END);
244162562Sjhb
245162562Sjhb	/* Wait for IBF = 0 */
246162562Sjhb	status = kcs_wait_for_ibf(sc, 0);
247162562Sjhb
248162562Sjhb	if (KCS_STATUS_STATE(status) != KCS_STATUS_STATE_WRITE)
249162562Sjhb		/* error state */
250162562Sjhb		return (0);
251162562Sjhb
252162562Sjhb	/* Clear OBF */
253162562Sjhb	kcs_clear_obf(sc, status);
254162562Sjhb
255162562Sjhb	/* Send data byte to DATA. */
256162562Sjhb	OUTB(sc, KCS_DATA, data);
257162562Sjhb	return (1);
258162562Sjhb}
259162562Sjhb
260162562Sjhb/*
261162562Sjhb * Read one byte of the reply message.
262162562Sjhb */
263162562Sjhbstatic int
264162562Sjhbkcs_read_byte(struct ipmi_softc *sc, u_char *data)
265162562Sjhb{
266162562Sjhb	int status;
267162562Sjhb	u_char dummy;
268162562Sjhb
269162562Sjhb	/* Wait for IBF = 0 */
270162562Sjhb	status = kcs_wait_for_ibf(sc, 0);
271162562Sjhb
272162562Sjhb	/* Read State */
273162562Sjhb	if (KCS_STATUS_STATE(status) == KCS_STATUS_STATE_READ) {
274162562Sjhb
275162562Sjhb		/* Wait for OBF = 1 */
276162562Sjhb		status = kcs_wait_for_obf(sc, 1);
277162562Sjhb
278162562Sjhb		/* Read Data_out */
279162562Sjhb		*data = INB(sc, KCS_DATA);
280162562Sjhb
281162562Sjhb		/* Write READ into Data_in */
282162562Sjhb		OUTB(sc, KCS_DATA, KCS_DATA_IN_READ);
283162562Sjhb		return (1);
284162562Sjhb	}
285162562Sjhb
286162562Sjhb	/* Idle State */
287162562Sjhb	if (KCS_STATUS_STATE(status) == KCS_STATUS_STATE_IDLE) {
288162562Sjhb
289162562Sjhb		/* Wait for OBF = 1*/
290162562Sjhb		status = kcs_wait_for_obf(sc, 1);
291162562Sjhb
292162562Sjhb		/* Read Dummy */
293162562Sjhb		dummy = INB(sc, KCS_DATA);
294162562Sjhb		return (2);
295162562Sjhb	}
296162562Sjhb
297162562Sjhb	/* Error State */
298162562Sjhb	return (0);
299162562Sjhb}
300162562Sjhb
301162562Sjhb/*
302162562Sjhb * Send a request message and collect the reply.  Returns true if we
303162562Sjhb * succeed.
304162562Sjhb */
305162562Sjhbstatic int
306162562Sjhbkcs_polled_request(struct ipmi_softc *sc, struct ipmi_request *req)
307162562Sjhb{
308162562Sjhb	u_char *cp, data;
309162562Sjhb	int i, state;
310162562Sjhb
311162562Sjhb	/* Send the request. */
312162562Sjhb	if (!kcs_start_write(sc)) {
313162562Sjhb		device_printf(sc->ipmi_dev, "KCS: Failed to start write\n");
314162562Sjhb		goto fail;
315162562Sjhb	}
316162562Sjhb#ifdef KCS_DEBUG
317162562Sjhb	device_printf(sc->ipmi_dev, "KCS: WRITE_START... ok\n");
318162562Sjhb#endif
319162562Sjhb
320162562Sjhb	if (!kcs_write_byte(sc, req->ir_addr)) {
321162562Sjhb		device_printf(sc->ipmi_dev, "KCS: Failed to write address\n");
322162562Sjhb		goto fail;
323162562Sjhb	}
324162562Sjhb#ifdef KCS_DEBUG
325162562Sjhb	device_printf(sc->ipmi_dev, "KCS: Wrote address: %02x\n", req->ir_addr);
326162562Sjhb#endif
327162562Sjhb
328162562Sjhb	if (req->ir_requestlen == 0) {
329162562Sjhb		if (!kcs_write_last_byte(sc, req->ir_command)) {
330162562Sjhb			device_printf(sc->ipmi_dev,
331162562Sjhb			    "KCS: Failed to write command\n");
332162562Sjhb			goto fail;
333162562Sjhb		}
334162562Sjhb#ifdef KCS_DEBUG
335162562Sjhb		device_printf(sc->ipmi_dev, "KCS: Wrote command: %02x\n",
336162562Sjhb		    req->ir_command);
337162562Sjhb#endif
338162562Sjhb	} else {
339162562Sjhb		if (!kcs_write_byte(sc, req->ir_command)) {
340162562Sjhb			device_printf(sc->ipmi_dev,
341162562Sjhb			    "KCS: Failed to write command\n");
342162562Sjhb			goto fail;
343162562Sjhb		}
344162562Sjhb#ifdef KCS_DEBUG
345162562Sjhb		device_printf(sc->ipmi_dev, "KCS: Wrote command: %02x\n",
346162562Sjhb		    req->ir_command);
347162562Sjhb#endif
348162562Sjhb
349162562Sjhb		cp = req->ir_request;
350162562Sjhb		for (i = 0; i < req->ir_requestlen - 1; i++) {
351162562Sjhb			if (!kcs_write_byte(sc, *cp++)) {
352162562Sjhb				device_printf(sc->ipmi_dev,
353162562Sjhb				    "KCS: Failed to write data byte %d\n",
354162562Sjhb				    i + 1);
355162562Sjhb				goto fail;
356162562Sjhb			}
357162562Sjhb#ifdef KCS_DEBUG
358162562Sjhb			device_printf(sc->ipmi_dev, "KCS: Wrote data: %02x\n",
359162562Sjhb			    cp[-1]);
360162562Sjhb#endif
361162562Sjhb		}
362162562Sjhb
363162562Sjhb		if (!kcs_write_last_byte(sc, *cp)) {
364162562Sjhb			device_printf(sc->ipmi_dev,
365162562Sjhb			    "KCS: Failed to write last dta byte\n");
366162562Sjhb			goto fail;
367162562Sjhb		}
368162562Sjhb#ifdef KCS_DEBUG
369162562Sjhb		device_printf(sc->ipmi_dev, "KCS: Wrote last data: %02x\n",
370162562Sjhb		    *cp);
371162562Sjhb#endif
372162562Sjhb	}
373162562Sjhb
374162562Sjhb	/* Read the reply.  First, read the NetFn/LUN. */
375162562Sjhb	if (kcs_read_byte(sc, &data) != 1) {
376162562Sjhb		device_printf(sc->ipmi_dev, "KCS: Failed to read address\n");
377162562Sjhb		goto fail;
378162562Sjhb	}
379162562Sjhb#ifdef KCS_DEBUG
380162562Sjhb	device_printf(sc->ipmi_dev, "KCS: Read address: %02x\n", data);
381162562Sjhb#endif
382162562Sjhb	if (data != IPMI_REPLY_ADDR(req->ir_addr)) {
383162562Sjhb		device_printf(sc->ipmi_dev, "KCS: Reply address mismatch\n");
384162562Sjhb		goto fail;
385162562Sjhb	}
386162562Sjhb
387162562Sjhb	/* Next we read the command. */
388162562Sjhb	if (kcs_read_byte(sc, &data) != 1) {
389162562Sjhb		device_printf(sc->ipmi_dev, "KCS: Failed to read command\n");
390162562Sjhb		goto fail;
391162562Sjhb	}
392162562Sjhb#ifdef KCS_DEBUG
393162562Sjhb	device_printf(sc->ipmi_dev, "KCS: Read command: %02x\n", data);
394162562Sjhb#endif
395162562Sjhb	if (data != req->ir_command) {
396162562Sjhb		device_printf(sc->ipmi_dev, "KCS: Command mismatch\n");
397162562Sjhb		goto fail;
398162562Sjhb	}
399162562Sjhb
400162562Sjhb	/* Next we read the completion code. */
401162562Sjhb	if (kcs_read_byte(sc, &req->ir_compcode) != 1) {
402162562Sjhb		device_printf(sc->ipmi_dev,
403162562Sjhb		    "KCS: Failed to read completion code\n");
404162562Sjhb		goto fail;
405162562Sjhb	}
406162562Sjhb#ifdef KCS_DEBUG
407162562Sjhb	device_printf(sc->ipmi_dev, "KCS: Read completion code: %02x\n",
408162562Sjhb	    req->ir_compcode);
409162562Sjhb#endif
410162562Sjhb
411162562Sjhb	/* Finally, read the reply from the BMC. */
412162562Sjhb	i = 0;
413162562Sjhb	for (;;) {
414162562Sjhb		state = kcs_read_byte(sc, &data);
415162562Sjhb		if (state == 0) {
416162562Sjhb			device_printf(sc->ipmi_dev,
417162562Sjhb			    "KCS: Read failed on byte %d\n", i + 1);
418162562Sjhb			goto fail;
419162562Sjhb		}
420162562Sjhb		if (state == 2)
421162562Sjhb			break;
422162562Sjhb		if (i < req->ir_replybuflen) {
423162562Sjhb			req->ir_reply[i] = data;
424162562Sjhb#ifdef KCS_DEBUG
425162562Sjhb			device_printf(sc->ipmi_dev, "KCS: Read data %02x\n",
426162562Sjhb			    data);
427162562Sjhb		} else {
428162562Sjhb			device_printf(sc->ipmi_dev,
429162562Sjhb			    "KCS: Read short %02x byte %d\n", data, i + 1);
430162562Sjhb#endif
431162562Sjhb		}
432162562Sjhb		i++;
433162562Sjhb	}
434162562Sjhb	req->ir_replylen = i;
435162562Sjhb#ifdef KCS_DEBUG
436162562Sjhb	device_printf(sc->ipmi_dev, "KCS: READ finished (%d bytes)\n", i);
437162562Sjhb	if (req->ir_replybuflen < i)
438162562Sjhb#else
439162562Sjhb	if (req->ir_replybuflen < i && req->ir_replybuflen != 0)
440162562Sjhb#endif
441162562Sjhb		device_printf(sc->ipmi_dev,
442162562Sjhb		    "KCS: Read short: %zd buffer, %d actual\n",
443162562Sjhb		    req->ir_replybuflen, i);
444162562Sjhb	return (1);
445162562Sjhbfail:
446162562Sjhb	kcs_error(sc);
447162562Sjhb	return (0);
448162562Sjhb}
449162562Sjhb
450162562Sjhbstatic void
451162562Sjhbkcs_loop(void *arg)
452162562Sjhb{
453162562Sjhb	struct ipmi_softc *sc = arg;
454162562Sjhb	struct ipmi_request *req;
455162562Sjhb	int i, ok;
456162562Sjhb
457162562Sjhb	IPMI_LOCK(sc);
458162562Sjhb	while ((req = ipmi_dequeue_request(sc)) != NULL) {
459162562Sjhb		ok = 0;
460162562Sjhb		for (i = 0; i < 3 && !ok; i++)
461162562Sjhb			ok = kcs_polled_request(sc, req);
462162562Sjhb		if (ok)
463162562Sjhb			req->ir_error = 0;
464162562Sjhb		else
465162562Sjhb			req->ir_error = EIO;
466162562Sjhb		ipmi_complete_request(sc, req);
467162562Sjhb	}
468162562Sjhb	IPMI_UNLOCK(sc);
469172836Sjulian	kproc_exit(0);
470162562Sjhb}
471162562Sjhb
472162562Sjhbstatic int
473162562Sjhbkcs_startup(struct ipmi_softc *sc)
474162562Sjhb{
475162562Sjhb
476172836Sjulian	return (kproc_create(kcs_loop, sc, &sc->ipmi_kthread, 0, 0, "%s: kcs",
477162562Sjhb	    device_get_nameunit(sc->ipmi_dev)));
478162562Sjhb}
479162562Sjhb
480162562Sjhbint
481162562Sjhbipmi_kcs_attach(struct ipmi_softc *sc)
482162562Sjhb{
483162562Sjhb	int status;
484162562Sjhb
485162562Sjhb	/* Setup function pointers. */
486162562Sjhb	sc->ipmi_startup = kcs_startup;
487162562Sjhb	sc->ipmi_enqueue_request = ipmi_polled_enqueue_request;
488162562Sjhb
489162562Sjhb	/* See if we can talk to the controller. */
490162562Sjhb	status = INB(sc, KCS_CTL_STS);
491162562Sjhb	if (status == 0xff) {
492162562Sjhb		device_printf(sc->ipmi_dev, "couldn't find it\n");
493162562Sjhb		return (ENXIO);
494162562Sjhb	}
495162562Sjhb
496162562Sjhb#ifdef KCS_DEBUG
497162562Sjhb	device_printf(sc->ipmi_dev, "KCS: initial state: %02x\n", status);
498162562Sjhb#endif
499162562Sjhb	if (status & KCS_STATUS_OBF ||
500162562Sjhb	    KCS_STATUS_STATE(status) != KCS_STATUS_STATE_IDLE)
501162562Sjhb		kcs_error(sc);
502162562Sjhb
503162562Sjhb	return (0);
504162562Sjhb}
505162562Sjhb
506162562Sjhb/*
507162562Sjhb * Determine the alignment automatically for a PCI attachment.  In this case,
508162562Sjhb * any unused bytes will return 0x00 when read.  We make use of the C/D bit
509162562Sjhb * in the CTL_STS register to try to start a GET_STATUS transaction.  When
510162562Sjhb * we write the command, that bit should be set, so we should get a non-zero
511162562Sjhb * value back when we read CTL_STS if the offset we are testing is the CTL_STS
512162562Sjhb * register.
513162562Sjhb */
514162562Sjhbint
515162562Sjhbipmi_kcs_probe_align(struct ipmi_softc *sc)
516162562Sjhb{
517162562Sjhb	int data, status;
518162562Sjhb
519162562Sjhb	sc->ipmi_io_spacing = 1;
520162562Sjhbretry:
521162562Sjhb#ifdef KCS_DEBUG
522162562Sjhb	device_printf(sc->ipmi_dev, "Trying KCS align %d... ", sc->ipmi_io_spacing);
523162562Sjhb#endif
524162562Sjhb
525162562Sjhb	/* Wait for IBF = 0 */
526162562Sjhb	status = INB(sc, KCS_CTL_STS);
527162562Sjhb	while (status & KCS_STATUS_IBF) {
528162562Sjhb		DELAY(100);
529162562Sjhb		status = INB(sc, KCS_CTL_STS);
530162562Sjhb	}
531162562Sjhb
532162562Sjhb	OUTB(sc, KCS_CTL_STS, KCS_CONTROL_GET_STATUS_ABORT);
533162562Sjhb
534162562Sjhb	/* Wait for IBF = 0 */
535162562Sjhb	status = INB(sc, KCS_CTL_STS);
536162562Sjhb	while (status & KCS_STATUS_IBF) {
537162562Sjhb		DELAY(100);
538162562Sjhb		status = INB(sc, KCS_CTL_STS);
539162562Sjhb	}
540162562Sjhb
541162562Sjhb	/* If we got 0x00 back, then this must not be the CTL_STS register. */
542162562Sjhb	if (status == 0) {
543162562Sjhb#ifdef KCS_DEBUG
544162562Sjhb		printf("failed\n");
545162562Sjhb#endif
546162562Sjhb		sc->ipmi_io_spacing <<= 1;
547162562Sjhb		if (sc->ipmi_io_spacing > 4)
548162562Sjhb			return (0);
549162562Sjhb		goto retry;
550162562Sjhb	}
551162562Sjhb#ifdef KCS_DEBUG
552162562Sjhb	printf("ok\n");
553162562Sjhb#endif
554162562Sjhb
555162562Sjhb	/* Finish out the transaction. */
556162562Sjhb
557162562Sjhb	/* Clear OBF */
558182321Sjhb	if (status & KCS_STATUS_OBF)
559162562Sjhb		data = INB(sc, KCS_DATA);
560162562Sjhb
561162562Sjhb	/* 0x00 to DATA_IN */
562162562Sjhb	OUTB(sc, KCS_DATA, 0);
563162562Sjhb
564162562Sjhb	/* Wait for IBF = 0 */
565162562Sjhb	status = INB(sc, KCS_CTL_STS);
566162562Sjhb	while (status & KCS_STATUS_IBF) {
567162562Sjhb		DELAY(100);
568162562Sjhb		status = INB(sc, KCS_CTL_STS);
569162562Sjhb	}
570162562Sjhb
571162562Sjhb	if (KCS_STATUS_STATE(status) == KCS_STATUS_STATE_READ) {
572162562Sjhb		/* Wait for IBF = 1 */
573162562Sjhb		while (!(status & KCS_STATUS_OBF)) {
574162562Sjhb			DELAY(100);
575162562Sjhb			status = INB(sc, KCS_CTL_STS);
576162562Sjhb		}
577162562Sjhb
578162562Sjhb		/* Read error status. */
579162562Sjhb		data = INB(sc, KCS_DATA);
580162562Sjhb
581162562Sjhb		/* Write dummy READ to DATA_IN. */
582162562Sjhb		OUTB(sc, KCS_DATA, KCS_DATA_IN_READ);
583162562Sjhb
584162562Sjhb		/* Wait for IBF = 0 */
585162562Sjhb		status = INB(sc, KCS_CTL_STS);
586162562Sjhb		while (status & KCS_STATUS_IBF) {
587162562Sjhb			DELAY(100);
588162562Sjhb			status = INB(sc, KCS_CTL_STS);
589162562Sjhb		}
590162562Sjhb	}
591162562Sjhb
592162562Sjhb	if (KCS_STATUS_STATE(status) == KCS_STATUS_STATE_IDLE) {
593162562Sjhb		/* Wait for IBF = 1 */
594162562Sjhb		while (!(status & KCS_STATUS_OBF)) {
595162562Sjhb			DELAY(100);
596162562Sjhb			status = INB(sc, KCS_CTL_STS);
597162562Sjhb		}
598162562Sjhb
599162562Sjhb		/* Clear OBF */
600182321Sjhb		if (status & KCS_STATUS_OBF)
601162562Sjhb			data = INB(sc, KCS_DATA);
602162562Sjhb	} else
603162562Sjhb		device_printf(sc->ipmi_dev, "KCS probe: end state %x\n",
604162562Sjhb		    KCS_STATUS_STATE(status));
605162562Sjhb
606162562Sjhb	return (1);
607162562Sjhb}
608