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);
187276065Sjhb		if (status & KCS_STATUS_IBF)
188276065Sjhb			return (0);
189162562Sjhb
190162562Sjhb		/* Clear OBF */
191162562Sjhb		kcs_clear_obf(sc, status);
192162562Sjhb
193162562Sjhb		/* Write start to command */
194162562Sjhb		OUTB(sc, KCS_CTL_STS, KCS_CONTROL_WRITE_START);
195162562Sjhb
196162562Sjhb		/* Wait for IBF = 0 */
197162562Sjhb		status = kcs_wait_for_ibf(sc, 0);
198276065Sjhb		if (status & KCS_STATUS_IBF)
199276065Sjhb			return (0);
200276065Sjhb
201162562Sjhb		if (KCS_STATUS_STATE(status) == KCS_STATUS_STATE_WRITE)
202162562Sjhb			break;
203162562Sjhb		DELAY(1000000);
204162562Sjhb	}
205162562Sjhb
206162562Sjhb	if (KCS_STATUS_STATE(status) != KCS_STATUS_STATE_WRITE)
207162562Sjhb		/* error state */
208162562Sjhb		return (0);
209162562Sjhb
210162562Sjhb	/* Clear OBF */
211162562Sjhb	kcs_clear_obf(sc, status);
212162562Sjhb
213162562Sjhb	return (1);
214162562Sjhb}
215162562Sjhb
216162562Sjhb/*
217162562Sjhb * Write a byte of the request message, excluding the last byte of the
218162562Sjhb * message which requires special handling.
219162562Sjhb */
220162562Sjhbstatic int
221162562Sjhbkcs_write_byte(struct ipmi_softc *sc, u_char data)
222162562Sjhb{
223162562Sjhb	int status;
224162562Sjhb
225162562Sjhb	/* Data to Data */
226162562Sjhb	OUTB(sc, KCS_DATA, data);
227162562Sjhb
228162562Sjhb	/* Wait for IBF = 0 */
229162562Sjhb	status = kcs_wait_for_ibf(sc, 0);
230276065Sjhb	if (status & KCS_STATUS_IBF)
231276065Sjhb		return (0);
232162562Sjhb
233162562Sjhb	if (KCS_STATUS_STATE(status) != KCS_STATUS_STATE_WRITE)
234162562Sjhb		return (0);
235162562Sjhb
236162562Sjhb	/* Clear OBF */
237162562Sjhb	kcs_clear_obf(sc, status);
238162562Sjhb	return (1);
239162562Sjhb}
240162562Sjhb
241162562Sjhb/*
242162562Sjhb * Write the last byte of a request message.
243162562Sjhb */
244162562Sjhbstatic int
245162562Sjhbkcs_write_last_byte(struct ipmi_softc *sc, u_char data)
246162562Sjhb{
247162562Sjhb	int status;
248162562Sjhb
249162562Sjhb	/* Write end to command */
250162562Sjhb	OUTB(sc, KCS_CTL_STS, KCS_CONTROL_WRITE_END);
251162562Sjhb
252162562Sjhb	/* Wait for IBF = 0 */
253162562Sjhb	status = kcs_wait_for_ibf(sc, 0);
254276065Sjhb	if (status & KCS_STATUS_IBF)
255276065Sjhb		return (0);
256162562Sjhb
257162562Sjhb	if (KCS_STATUS_STATE(status) != KCS_STATUS_STATE_WRITE)
258162562Sjhb		/* error state */
259162562Sjhb		return (0);
260162562Sjhb
261162562Sjhb	/* Clear OBF */
262162562Sjhb	kcs_clear_obf(sc, status);
263162562Sjhb
264162562Sjhb	/* Send data byte to DATA. */
265162562Sjhb	OUTB(sc, KCS_DATA, data);
266162562Sjhb	return (1);
267162562Sjhb}
268162562Sjhb
269162562Sjhb/*
270162562Sjhb * Read one byte of the reply message.
271162562Sjhb */
272162562Sjhbstatic int
273162562Sjhbkcs_read_byte(struct ipmi_softc *sc, u_char *data)
274162562Sjhb{
275162562Sjhb	int status;
276162562Sjhb	u_char dummy;
277162562Sjhb
278162562Sjhb	/* Wait for IBF = 0 */
279162562Sjhb	status = kcs_wait_for_ibf(sc, 0);
280162562Sjhb
281162562Sjhb	/* Read State */
282162562Sjhb	if (KCS_STATUS_STATE(status) == KCS_STATUS_STATE_READ) {
283162562Sjhb
284162562Sjhb		/* Wait for OBF = 1 */
285162562Sjhb		status = kcs_wait_for_obf(sc, 1);
286276065Sjhb		if ((status & KCS_STATUS_OBF) == 0)
287276065Sjhb			return (0);
288162562Sjhb
289162562Sjhb		/* Read Data_out */
290162562Sjhb		*data = INB(sc, KCS_DATA);
291162562Sjhb
292162562Sjhb		/* Write READ into Data_in */
293162562Sjhb		OUTB(sc, KCS_DATA, KCS_DATA_IN_READ);
294162562Sjhb		return (1);
295162562Sjhb	}
296162562Sjhb
297162562Sjhb	/* Idle State */
298162562Sjhb	if (KCS_STATUS_STATE(status) == KCS_STATUS_STATE_IDLE) {
299162562Sjhb
300162562Sjhb		/* Wait for OBF = 1*/
301162562Sjhb		status = kcs_wait_for_obf(sc, 1);
302276065Sjhb		if ((status & KCS_STATUS_OBF) == 0)
303276065Sjhb			return (0);
304162562Sjhb
305162562Sjhb		/* Read Dummy */
306162562Sjhb		dummy = INB(sc, KCS_DATA);
307162562Sjhb		return (2);
308162562Sjhb	}
309162562Sjhb
310162562Sjhb	/* Error State */
311162562Sjhb	return (0);
312162562Sjhb}
313162562Sjhb
314162562Sjhb/*
315162562Sjhb * Send a request message and collect the reply.  Returns true if we
316162562Sjhb * succeed.
317162562Sjhb */
318162562Sjhbstatic int
319162562Sjhbkcs_polled_request(struct ipmi_softc *sc, struct ipmi_request *req)
320162562Sjhb{
321162562Sjhb	u_char *cp, data;
322162562Sjhb	int i, state;
323162562Sjhb
324278321Sjhb	IPMI_IO_LOCK(sc);
325278321Sjhb
326162562Sjhb	/* Send the request. */
327162562Sjhb	if (!kcs_start_write(sc)) {
328162562Sjhb		device_printf(sc->ipmi_dev, "KCS: Failed to start write\n");
329162562Sjhb		goto fail;
330162562Sjhb	}
331162562Sjhb#ifdef KCS_DEBUG
332162562Sjhb	device_printf(sc->ipmi_dev, "KCS: WRITE_START... ok\n");
333162562Sjhb#endif
334162562Sjhb
335162562Sjhb	if (!kcs_write_byte(sc, req->ir_addr)) {
336162562Sjhb		device_printf(sc->ipmi_dev, "KCS: Failed to write address\n");
337162562Sjhb		goto fail;
338162562Sjhb	}
339162562Sjhb#ifdef KCS_DEBUG
340162562Sjhb	device_printf(sc->ipmi_dev, "KCS: Wrote address: %02x\n", req->ir_addr);
341162562Sjhb#endif
342162562Sjhb
343162562Sjhb	if (req->ir_requestlen == 0) {
344162562Sjhb		if (!kcs_write_last_byte(sc, req->ir_command)) {
345162562Sjhb			device_printf(sc->ipmi_dev,
346162562Sjhb			    "KCS: Failed to write command\n");
347162562Sjhb			goto fail;
348162562Sjhb		}
349162562Sjhb#ifdef KCS_DEBUG
350162562Sjhb		device_printf(sc->ipmi_dev, "KCS: Wrote command: %02x\n",
351162562Sjhb		    req->ir_command);
352162562Sjhb#endif
353162562Sjhb	} else {
354162562Sjhb		if (!kcs_write_byte(sc, req->ir_command)) {
355162562Sjhb			device_printf(sc->ipmi_dev,
356162562Sjhb			    "KCS: Failed to write command\n");
357162562Sjhb			goto fail;
358162562Sjhb		}
359162562Sjhb#ifdef KCS_DEBUG
360162562Sjhb		device_printf(sc->ipmi_dev, "KCS: Wrote command: %02x\n",
361162562Sjhb		    req->ir_command);
362162562Sjhb#endif
363162562Sjhb
364162562Sjhb		cp = req->ir_request;
365162562Sjhb		for (i = 0; i < req->ir_requestlen - 1; i++) {
366162562Sjhb			if (!kcs_write_byte(sc, *cp++)) {
367162562Sjhb				device_printf(sc->ipmi_dev,
368162562Sjhb				    "KCS: Failed to write data byte %d\n",
369162562Sjhb				    i + 1);
370162562Sjhb				goto fail;
371162562Sjhb			}
372162562Sjhb#ifdef KCS_DEBUG
373162562Sjhb			device_printf(sc->ipmi_dev, "KCS: Wrote data: %02x\n",
374162562Sjhb			    cp[-1]);
375162562Sjhb#endif
376162562Sjhb		}
377162562Sjhb
378162562Sjhb		if (!kcs_write_last_byte(sc, *cp)) {
379162562Sjhb			device_printf(sc->ipmi_dev,
380162562Sjhb			    "KCS: Failed to write last dta byte\n");
381162562Sjhb			goto fail;
382162562Sjhb		}
383162562Sjhb#ifdef KCS_DEBUG
384162562Sjhb		device_printf(sc->ipmi_dev, "KCS: Wrote last data: %02x\n",
385162562Sjhb		    *cp);
386162562Sjhb#endif
387162562Sjhb	}
388162562Sjhb
389162562Sjhb	/* Read the reply.  First, read the NetFn/LUN. */
390162562Sjhb	if (kcs_read_byte(sc, &data) != 1) {
391162562Sjhb		device_printf(sc->ipmi_dev, "KCS: Failed to read address\n");
392162562Sjhb		goto fail;
393162562Sjhb	}
394162562Sjhb#ifdef KCS_DEBUG
395162562Sjhb	device_printf(sc->ipmi_dev, "KCS: Read address: %02x\n", data);
396162562Sjhb#endif
397162562Sjhb	if (data != IPMI_REPLY_ADDR(req->ir_addr)) {
398162562Sjhb		device_printf(sc->ipmi_dev, "KCS: Reply address mismatch\n");
399162562Sjhb		goto fail;
400162562Sjhb	}
401162562Sjhb
402162562Sjhb	/* Next we read the command. */
403162562Sjhb	if (kcs_read_byte(sc, &data) != 1) {
404162562Sjhb		device_printf(sc->ipmi_dev, "KCS: Failed to read command\n");
405162562Sjhb		goto fail;
406162562Sjhb	}
407162562Sjhb#ifdef KCS_DEBUG
408162562Sjhb	device_printf(sc->ipmi_dev, "KCS: Read command: %02x\n", data);
409162562Sjhb#endif
410162562Sjhb	if (data != req->ir_command) {
411162562Sjhb		device_printf(sc->ipmi_dev, "KCS: Command mismatch\n");
412162562Sjhb		goto fail;
413162562Sjhb	}
414162562Sjhb
415162562Sjhb	/* Next we read the completion code. */
416162562Sjhb	if (kcs_read_byte(sc, &req->ir_compcode) != 1) {
417162562Sjhb		device_printf(sc->ipmi_dev,
418162562Sjhb		    "KCS: Failed to read completion code\n");
419162562Sjhb		goto fail;
420162562Sjhb	}
421162562Sjhb#ifdef KCS_DEBUG
422162562Sjhb	device_printf(sc->ipmi_dev, "KCS: Read completion code: %02x\n",
423162562Sjhb	    req->ir_compcode);
424162562Sjhb#endif
425162562Sjhb
426162562Sjhb	/* Finally, read the reply from the BMC. */
427162562Sjhb	i = 0;
428162562Sjhb	for (;;) {
429162562Sjhb		state = kcs_read_byte(sc, &data);
430162562Sjhb		if (state == 0) {
431162562Sjhb			device_printf(sc->ipmi_dev,
432162562Sjhb			    "KCS: Read failed on byte %d\n", i + 1);
433162562Sjhb			goto fail;
434162562Sjhb		}
435162562Sjhb		if (state == 2)
436162562Sjhb			break;
437162562Sjhb		if (i < req->ir_replybuflen) {
438162562Sjhb			req->ir_reply[i] = data;
439162562Sjhb#ifdef KCS_DEBUG
440162562Sjhb			device_printf(sc->ipmi_dev, "KCS: Read data %02x\n",
441162562Sjhb			    data);
442162562Sjhb		} else {
443162562Sjhb			device_printf(sc->ipmi_dev,
444162562Sjhb			    "KCS: Read short %02x byte %d\n", data, i + 1);
445162562Sjhb#endif
446162562Sjhb		}
447162562Sjhb		i++;
448162562Sjhb	}
449278321Sjhb	IPMI_IO_UNLOCK(sc);
450162562Sjhb	req->ir_replylen = i;
451162562Sjhb#ifdef KCS_DEBUG
452162562Sjhb	device_printf(sc->ipmi_dev, "KCS: READ finished (%d bytes)\n", i);
453162562Sjhb	if (req->ir_replybuflen < i)
454162562Sjhb#else
455162562Sjhb	if (req->ir_replybuflen < i && req->ir_replybuflen != 0)
456162562Sjhb#endif
457162562Sjhb		device_printf(sc->ipmi_dev,
458162562Sjhb		    "KCS: Read short: %zd buffer, %d actual\n",
459162562Sjhb		    req->ir_replybuflen, i);
460162562Sjhb	return (1);
461162562Sjhbfail:
462162562Sjhb	kcs_error(sc);
463278321Sjhb	IPMI_IO_UNLOCK(sc);
464162562Sjhb	return (0);
465162562Sjhb}
466162562Sjhb
467162562Sjhbstatic void
468162562Sjhbkcs_loop(void *arg)
469162562Sjhb{
470162562Sjhb	struct ipmi_softc *sc = arg;
471162562Sjhb	struct ipmi_request *req;
472162562Sjhb	int i, ok;
473162562Sjhb
474162562Sjhb	IPMI_LOCK(sc);
475162562Sjhb	while ((req = ipmi_dequeue_request(sc)) != NULL) {
476248705Smelifaro		IPMI_UNLOCK(sc);
477162562Sjhb		ok = 0;
478162562Sjhb		for (i = 0; i < 3 && !ok; i++)
479162562Sjhb			ok = kcs_polled_request(sc, req);
480162562Sjhb		if (ok)
481162562Sjhb			req->ir_error = 0;
482162562Sjhb		else
483162562Sjhb			req->ir_error = EIO;
484248705Smelifaro		IPMI_LOCK(sc);
485162562Sjhb		ipmi_complete_request(sc, req);
486162562Sjhb	}
487162562Sjhb	IPMI_UNLOCK(sc);
488172836Sjulian	kproc_exit(0);
489162562Sjhb}
490162562Sjhb
491162562Sjhbstatic int
492162562Sjhbkcs_startup(struct ipmi_softc *sc)
493162562Sjhb{
494162562Sjhb
495172836Sjulian	return (kproc_create(kcs_loop, sc, &sc->ipmi_kthread, 0, 0, "%s: kcs",
496162562Sjhb	    device_get_nameunit(sc->ipmi_dev)));
497162562Sjhb}
498162562Sjhb
499278321Sjhbstatic int
500278321Sjhbkcs_driver_request(struct ipmi_softc *sc, struct ipmi_request *req, int timo)
501278321Sjhb{
502278321Sjhb	int i, ok;
503278321Sjhb
504278321Sjhb	ok = 0;
505278321Sjhb	for (i = 0; i < 3 && !ok; i++)
506278321Sjhb		ok = kcs_polled_request(sc, req);
507278321Sjhb	if (ok)
508278321Sjhb		req->ir_error = 0;
509278321Sjhb	else
510278321Sjhb		req->ir_error = EIO;
511278321Sjhb	return (req->ir_error);
512278321Sjhb}
513278321Sjhb
514162562Sjhbint
515162562Sjhbipmi_kcs_attach(struct ipmi_softc *sc)
516162562Sjhb{
517162562Sjhb	int status;
518162562Sjhb
519162562Sjhb	/* Setup function pointers. */
520162562Sjhb	sc->ipmi_startup = kcs_startup;
521162562Sjhb	sc->ipmi_enqueue_request = ipmi_polled_enqueue_request;
522278321Sjhb	sc->ipmi_driver_request = kcs_driver_request;
523281941Sjhb	sc->ipmi_driver_requests_polled = 1;
524162562Sjhb
525162562Sjhb	/* See if we can talk to the controller. */
526162562Sjhb	status = INB(sc, KCS_CTL_STS);
527162562Sjhb	if (status == 0xff) {
528162562Sjhb		device_printf(sc->ipmi_dev, "couldn't find it\n");
529162562Sjhb		return (ENXIO);
530162562Sjhb	}
531162562Sjhb
532162562Sjhb#ifdef KCS_DEBUG
533162562Sjhb	device_printf(sc->ipmi_dev, "KCS: initial state: %02x\n", status);
534162562Sjhb#endif
535162562Sjhb	if (status & KCS_STATUS_OBF ||
536162562Sjhb	    KCS_STATUS_STATE(status) != KCS_STATUS_STATE_IDLE)
537162562Sjhb		kcs_error(sc);
538162562Sjhb
539162562Sjhb	return (0);
540162562Sjhb}
541162562Sjhb
542162562Sjhb/*
543162562Sjhb * Determine the alignment automatically for a PCI attachment.  In this case,
544162562Sjhb * any unused bytes will return 0x00 when read.  We make use of the C/D bit
545162562Sjhb * in the CTL_STS register to try to start a GET_STATUS transaction.  When
546162562Sjhb * we write the command, that bit should be set, so we should get a non-zero
547162562Sjhb * value back when we read CTL_STS if the offset we are testing is the CTL_STS
548162562Sjhb * register.
549162562Sjhb */
550162562Sjhbint
551162562Sjhbipmi_kcs_probe_align(struct ipmi_softc *sc)
552162562Sjhb{
553162562Sjhb	int data, status;
554162562Sjhb
555162562Sjhb	sc->ipmi_io_spacing = 1;
556162562Sjhbretry:
557162562Sjhb#ifdef KCS_DEBUG
558162562Sjhb	device_printf(sc->ipmi_dev, "Trying KCS align %d... ", sc->ipmi_io_spacing);
559162562Sjhb#endif
560162562Sjhb
561162562Sjhb	/* Wait for IBF = 0 */
562162562Sjhb	status = INB(sc, KCS_CTL_STS);
563162562Sjhb	while (status & KCS_STATUS_IBF) {
564162562Sjhb		DELAY(100);
565162562Sjhb		status = INB(sc, KCS_CTL_STS);
566162562Sjhb	}
567162562Sjhb
568162562Sjhb	OUTB(sc, KCS_CTL_STS, KCS_CONTROL_GET_STATUS_ABORT);
569162562Sjhb
570162562Sjhb	/* Wait for IBF = 0 */
571162562Sjhb	status = INB(sc, KCS_CTL_STS);
572162562Sjhb	while (status & KCS_STATUS_IBF) {
573162562Sjhb		DELAY(100);
574162562Sjhb		status = INB(sc, KCS_CTL_STS);
575162562Sjhb	}
576162562Sjhb
577162562Sjhb	/* If we got 0x00 back, then this must not be the CTL_STS register. */
578162562Sjhb	if (status == 0) {
579162562Sjhb#ifdef KCS_DEBUG
580162562Sjhb		printf("failed\n");
581162562Sjhb#endif
582162562Sjhb		sc->ipmi_io_spacing <<= 1;
583162562Sjhb		if (sc->ipmi_io_spacing > 4)
584162562Sjhb			return (0);
585162562Sjhb		goto retry;
586162562Sjhb	}
587162562Sjhb#ifdef KCS_DEBUG
588162562Sjhb	printf("ok\n");
589162562Sjhb#endif
590162562Sjhb
591162562Sjhb	/* Finish out the transaction. */
592162562Sjhb
593162562Sjhb	/* Clear OBF */
594182321Sjhb	if (status & KCS_STATUS_OBF)
595162562Sjhb		data = INB(sc, KCS_DATA);
596162562Sjhb
597162562Sjhb	/* 0x00 to DATA_IN */
598162562Sjhb	OUTB(sc, KCS_DATA, 0);
599162562Sjhb
600162562Sjhb	/* Wait for IBF = 0 */
601162562Sjhb	status = INB(sc, KCS_CTL_STS);
602162562Sjhb	while (status & KCS_STATUS_IBF) {
603162562Sjhb		DELAY(100);
604162562Sjhb		status = INB(sc, KCS_CTL_STS);
605162562Sjhb	}
606162562Sjhb
607162562Sjhb	if (KCS_STATUS_STATE(status) == KCS_STATUS_STATE_READ) {
608162562Sjhb		/* Wait for IBF = 1 */
609162562Sjhb		while (!(status & KCS_STATUS_OBF)) {
610162562Sjhb			DELAY(100);
611162562Sjhb			status = INB(sc, KCS_CTL_STS);
612162562Sjhb		}
613162562Sjhb
614162562Sjhb		/* Read error status. */
615162562Sjhb		data = INB(sc, KCS_DATA);
616162562Sjhb
617162562Sjhb		/* Write dummy READ to DATA_IN. */
618162562Sjhb		OUTB(sc, KCS_DATA, KCS_DATA_IN_READ);
619162562Sjhb
620162562Sjhb		/* Wait for IBF = 0 */
621162562Sjhb		status = INB(sc, KCS_CTL_STS);
622162562Sjhb		while (status & KCS_STATUS_IBF) {
623162562Sjhb			DELAY(100);
624162562Sjhb			status = INB(sc, KCS_CTL_STS);
625162562Sjhb		}
626162562Sjhb	}
627162562Sjhb
628162562Sjhb	if (KCS_STATUS_STATE(status) == KCS_STATUS_STATE_IDLE) {
629162562Sjhb		/* Wait for IBF = 1 */
630162562Sjhb		while (!(status & KCS_STATUS_OBF)) {
631162562Sjhb			DELAY(100);
632162562Sjhb			status = INB(sc, KCS_CTL_STS);
633162562Sjhb		}
634162562Sjhb
635162562Sjhb		/* Clear OBF */
636182321Sjhb		if (status & KCS_STATUS_OBF)
637162562Sjhb			data = INB(sc, KCS_DATA);
638162562Sjhb	} else
639162562Sjhb		device_printf(sc->ipmi_dev, "KCS probe: end state %x\n",
640162562Sjhb		    KCS_STATUS_STATE(status));
641162562Sjhb
642162562Sjhb	return (1);
643162562Sjhb}
644