xref: /illumos-gate/usr/src/uts/intel/io/ipmi/ipmi_kcs.c (revision fc8ae2ec)
1989f2807SJerry Jelinek /*
2989f2807SJerry Jelinek  * Copyright (c) 2006 IronPort Systems Inc. <ambrisko@ironport.com>
3989f2807SJerry Jelinek  * All rights reserved.
4989f2807SJerry Jelinek  *
5989f2807SJerry Jelinek  * Redistribution and use in source and binary forms, with or without
6989f2807SJerry Jelinek  * modification, are permitted provided that the following conditions
7989f2807SJerry Jelinek  * are met:
8989f2807SJerry Jelinek  * 1. Redistributions of source code must retain the above copyright
9989f2807SJerry Jelinek  *    notice, this list of conditions and the following disclaimer.
10989f2807SJerry Jelinek  * 2. Redistributions in binary form must reproduce the above copyright
11989f2807SJerry Jelinek  *    notice, this list of conditions and the following disclaimer in the
12989f2807SJerry Jelinek  *    documentation and/or other materials provided with the distribution.
13989f2807SJerry Jelinek  *
14989f2807SJerry Jelinek  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15989f2807SJerry Jelinek  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16989f2807SJerry Jelinek  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17989f2807SJerry Jelinek  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18989f2807SJerry Jelinek  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19989f2807SJerry Jelinek  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20989f2807SJerry Jelinek  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21989f2807SJerry Jelinek  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22989f2807SJerry Jelinek  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23989f2807SJerry Jelinek  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24989f2807SJerry Jelinek  * SUCH DAMAGE.
25989f2807SJerry Jelinek  */
26989f2807SJerry Jelinek 
27989f2807SJerry Jelinek /* $FreeBSD: src/sys/dev/ipmi/ipmi_kcs.c,v 1.3 2008/08/28 02:11:04 jhb */
28989f2807SJerry Jelinek 
29989f2807SJerry Jelinek /*
3012950e8eSJerry Jelinek  * Copyright 2013, Joyent, Inc.  All rights reserved.
311e393477SMarcel Telka  * Copyright 2013 Nexenta Systems, Inc.  All rights reserved.
32989f2807SJerry Jelinek  */
33989f2807SJerry Jelinek 
34989f2807SJerry Jelinek #include <sys/param.h>
35989f2807SJerry Jelinek #include <sys/disp.h>
36989f2807SJerry Jelinek #include <sys/systm.h>
37989f2807SJerry Jelinek #include <sys/condvar.h>
38989f2807SJerry Jelinek #include <sys/cmn_err.h>
39989f2807SJerry Jelinek #include <sys/ddi.h>
40989f2807SJerry Jelinek #include <sys/sunddi.h>
41989f2807SJerry Jelinek 
42989f2807SJerry Jelinek #include <sys/ipmi.h>
43989f2807SJerry Jelinek #include "ipmivars.h"
44989f2807SJerry Jelinek 
45989f2807SJerry Jelinek static void	kcs_clear_obf(struct ipmi_softc *, int);
46989f2807SJerry Jelinek static void	kcs_error(struct ipmi_softc *);
47989f2807SJerry Jelinek static int	kcs_wait_for_ibf(struct ipmi_softc *, int);
48989f2807SJerry Jelinek static int	kcs_wait_for_obf(struct ipmi_softc *, int);
49989f2807SJerry Jelinek 
50989f2807SJerry Jelinek #define	RETRY_USECS	100
51989f2807SJerry Jelinek static clock_t timeout_usecs;
52989f2807SJerry Jelinek 
53989f2807SJerry Jelinek static int
kcs_wait_for_ibf(struct ipmi_softc * sc,int state)54989f2807SJerry Jelinek kcs_wait_for_ibf(struct ipmi_softc *sc, int state)
55989f2807SJerry Jelinek {
56989f2807SJerry Jelinek 	int status;
57989f2807SJerry Jelinek 	clock_t i;
58989f2807SJerry Jelinek 
59989f2807SJerry Jelinek 	status = INB(sc, KCS_CTL_STS);
60989f2807SJerry Jelinek 	if (state == 0) {
61989f2807SJerry Jelinek 		/* WAIT FOR IBF = 0 */
62989f2807SJerry Jelinek 		for (i = 0; i < timeout_usecs && status & KCS_STATUS_IBF;
63989f2807SJerry Jelinek 		    i += RETRY_USECS) {
64989f2807SJerry Jelinek 			drv_usecwait(RETRY_USECS);
65989f2807SJerry Jelinek 			status = INB(sc, KCS_CTL_STS);
66989f2807SJerry Jelinek 		}
67989f2807SJerry Jelinek 	} else {
68989f2807SJerry Jelinek 		/* WAIT FOR IBF = 1 */
69989f2807SJerry Jelinek 		for (i = 0; i < timeout_usecs && !(status & KCS_STATUS_IBF);
70989f2807SJerry Jelinek 		    i += RETRY_USECS) {
71989f2807SJerry Jelinek 			drv_usecwait(RETRY_USECS);
72989f2807SJerry Jelinek 			status = INB(sc, KCS_CTL_STS);
73989f2807SJerry Jelinek 		}
74989f2807SJerry Jelinek 	}
75989f2807SJerry Jelinek 	return (status);
76989f2807SJerry Jelinek }
77989f2807SJerry Jelinek 
78989f2807SJerry Jelinek static int
kcs_wait_for_obf(struct ipmi_softc * sc,int state)79989f2807SJerry Jelinek kcs_wait_for_obf(struct ipmi_softc *sc, int state)
80989f2807SJerry Jelinek {
81989f2807SJerry Jelinek 	int status;
82989f2807SJerry Jelinek 	clock_t i;
83989f2807SJerry Jelinek 
84989f2807SJerry Jelinek 	status = INB(sc, KCS_CTL_STS);
85989f2807SJerry Jelinek 	if (state == 0) {
86989f2807SJerry Jelinek 		/* WAIT FOR OBF = 0 */
87989f2807SJerry Jelinek 		for (i = 0; i < timeout_usecs && status & KCS_STATUS_OBF;
88989f2807SJerry Jelinek 		    i += RETRY_USECS) {
89989f2807SJerry Jelinek 			drv_usecwait(RETRY_USECS);
90989f2807SJerry Jelinek 			status = INB(sc, KCS_CTL_STS);
91989f2807SJerry Jelinek 		}
92989f2807SJerry Jelinek 	} else {
93989f2807SJerry Jelinek 		/* WAIT FOR OBF = 1 */
94989f2807SJerry Jelinek 		for (i = 0; i < timeout_usecs && !(status & KCS_STATUS_OBF);
95989f2807SJerry Jelinek 		    i += RETRY_USECS) {
96989f2807SJerry Jelinek 			drv_usecwait(RETRY_USECS);
97989f2807SJerry Jelinek 			status = INB(sc, KCS_CTL_STS);
98989f2807SJerry Jelinek 		}
99989f2807SJerry Jelinek 	}
100989f2807SJerry Jelinek 	return (status);
101989f2807SJerry Jelinek }
102989f2807SJerry Jelinek 
103989f2807SJerry Jelinek static void
kcs_clear_obf(struct ipmi_softc * sc,int status)104989f2807SJerry Jelinek kcs_clear_obf(struct ipmi_softc *sc, int status)
105989f2807SJerry Jelinek {
106989f2807SJerry Jelinek 	/* Clear OBF */
107989f2807SJerry Jelinek 	if (status & KCS_STATUS_OBF) {
108989f2807SJerry Jelinek 		(void) INB(sc, KCS_DATA);
109989f2807SJerry Jelinek 	}
110989f2807SJerry Jelinek }
111989f2807SJerry Jelinek 
112989f2807SJerry Jelinek static void
kcs_error(struct ipmi_softc * sc)113989f2807SJerry Jelinek kcs_error(struct ipmi_softc *sc)
114989f2807SJerry Jelinek {
115989f2807SJerry Jelinek 	int retry, status;
116989f2807SJerry Jelinek 	uchar_t data;
117989f2807SJerry Jelinek 
118989f2807SJerry Jelinek 	for (retry = 0; retry < 2; retry++) {
119989f2807SJerry Jelinek 
120989f2807SJerry Jelinek 		/* Wait for IBF = 0 */
121989f2807SJerry Jelinek 		status = kcs_wait_for_ibf(sc, 0);
122989f2807SJerry Jelinek 
123989f2807SJerry Jelinek 		/* ABORT */
124989f2807SJerry Jelinek 		OUTB(sc, KCS_CTL_STS, KCS_CONTROL_GET_STATUS_ABORT);
125989f2807SJerry Jelinek 
126989f2807SJerry Jelinek 		/* Wait for IBF = 0 */
127989f2807SJerry Jelinek 		status = kcs_wait_for_ibf(sc, 0);
128989f2807SJerry Jelinek 
129989f2807SJerry Jelinek 		/* Clear OBF */
130989f2807SJerry Jelinek 		kcs_clear_obf(sc, status);
131989f2807SJerry Jelinek 
132989f2807SJerry Jelinek 		if (status & KCS_STATUS_OBF) {
133989f2807SJerry Jelinek 			data = INB(sc, KCS_DATA);
134989f2807SJerry Jelinek 			if (data != 0)
135989f2807SJerry Jelinek 				cmn_err(CE_WARN,
136989f2807SJerry Jelinek 				    "KCS Error Data %02x", data);
137989f2807SJerry Jelinek 		}
138989f2807SJerry Jelinek 
139989f2807SJerry Jelinek 		/* 0x00 to DATA_IN */
140989f2807SJerry Jelinek 		OUTB(sc, KCS_DATA, 0x00);
141989f2807SJerry Jelinek 
142989f2807SJerry Jelinek 		/* Wait for IBF = 0 */
143989f2807SJerry Jelinek 		status = kcs_wait_for_ibf(sc, 0);
144989f2807SJerry Jelinek 
145989f2807SJerry Jelinek 		if (KCS_STATUS_STATE(status) == KCS_STATUS_STATE_READ) {
146989f2807SJerry Jelinek 
147989f2807SJerry Jelinek 			/* Wait for OBF = 1 */
148989f2807SJerry Jelinek 			status = kcs_wait_for_obf(sc, 1);
149989f2807SJerry Jelinek 
150989f2807SJerry Jelinek 			/* Read error status */
151989f2807SJerry Jelinek 			data = INB(sc, KCS_DATA);
152989f2807SJerry Jelinek 			if (data != 0)
153989f2807SJerry Jelinek 				cmn_err(CE_WARN, "KCS error: %02x", data);
154989f2807SJerry Jelinek 
155989f2807SJerry Jelinek 			/* Write READ into Data_in */
156989f2807SJerry Jelinek 			OUTB(sc, KCS_DATA, KCS_DATA_IN_READ);
157989f2807SJerry Jelinek 
158989f2807SJerry Jelinek 			/* Wait for IBF = 0 */
159989f2807SJerry Jelinek 			status = kcs_wait_for_ibf(sc, 0);
160989f2807SJerry Jelinek 		}
161989f2807SJerry Jelinek 
162989f2807SJerry Jelinek 		/* IDLE STATE */
163989f2807SJerry Jelinek 		if (KCS_STATUS_STATE(status) == KCS_STATUS_STATE_IDLE) {
164989f2807SJerry Jelinek 			/* Wait for OBF = 1 */
165989f2807SJerry Jelinek 			status = kcs_wait_for_obf(sc, 1);
166989f2807SJerry Jelinek 
167989f2807SJerry Jelinek 			/* Clear OBF */
168989f2807SJerry Jelinek 			kcs_clear_obf(sc, status);
169989f2807SJerry Jelinek 			return;
170989f2807SJerry Jelinek 		}
171989f2807SJerry Jelinek 	}
172989f2807SJerry Jelinek 	cmn_err(CE_WARN, "KCS: Error retry exhausted");
173989f2807SJerry Jelinek }
174989f2807SJerry Jelinek 
175989f2807SJerry Jelinek /*
176989f2807SJerry Jelinek  * Start to write a request.  Waits for IBF to clear and then sends the
177989f2807SJerry Jelinek  * WR_START command.
178989f2807SJerry Jelinek  */
179989f2807SJerry Jelinek static int
kcs_start_write(struct ipmi_softc * sc)180989f2807SJerry Jelinek kcs_start_write(struct ipmi_softc *sc)
181989f2807SJerry Jelinek {
182989f2807SJerry Jelinek 	int retry, status;
183989f2807SJerry Jelinek 
184989f2807SJerry Jelinek 	for (retry = 0; retry < 10; retry++) {
185989f2807SJerry Jelinek 		/* Wait for IBF = 0 */
186989f2807SJerry Jelinek 		status = kcs_wait_for_ibf(sc, 0);
187989f2807SJerry Jelinek 
188989f2807SJerry Jelinek 		/* Clear OBF */
189989f2807SJerry Jelinek 		kcs_clear_obf(sc, status);
190989f2807SJerry Jelinek 
191989f2807SJerry Jelinek 		/* Write start to command */
192989f2807SJerry Jelinek 		OUTB(sc, KCS_CTL_STS, KCS_CONTROL_WRITE_START);
193989f2807SJerry Jelinek 
194989f2807SJerry Jelinek 		/* Wait for IBF = 0 */
195989f2807SJerry Jelinek 		status = kcs_wait_for_ibf(sc, 0);
196989f2807SJerry Jelinek 		if (KCS_STATUS_STATE(status) == KCS_STATUS_STATE_WRITE)
197989f2807SJerry Jelinek 			break;
19812950e8eSJerry Jelinek 		delay(drv_usectohz(1000000));
199989f2807SJerry Jelinek 	}
200989f2807SJerry Jelinek 
201989f2807SJerry Jelinek 	if (KCS_STATUS_STATE(status) != KCS_STATUS_STATE_WRITE)
202989f2807SJerry Jelinek 		/* error state */
203989f2807SJerry Jelinek 		return (0);
204989f2807SJerry Jelinek 
205989f2807SJerry Jelinek 	/* Clear OBF */
206989f2807SJerry Jelinek 	kcs_clear_obf(sc, status);
207989f2807SJerry Jelinek 
208989f2807SJerry Jelinek 	return (1);
209989f2807SJerry Jelinek }
210989f2807SJerry Jelinek 
211989f2807SJerry Jelinek /*
212989f2807SJerry Jelinek  * Write a byte of the request message, excluding the last byte of the
213989f2807SJerry Jelinek  * message which requires special handling.
214989f2807SJerry Jelinek  */
215989f2807SJerry Jelinek static int
kcs_write_byte(struct ipmi_softc * sc,uchar_t data)216989f2807SJerry Jelinek kcs_write_byte(struct ipmi_softc *sc, uchar_t data)
217989f2807SJerry Jelinek {
218989f2807SJerry Jelinek 	int status;
219989f2807SJerry Jelinek 
220989f2807SJerry Jelinek 	/* Data to Data */
221989f2807SJerry Jelinek 	OUTB(sc, KCS_DATA, data);
222989f2807SJerry Jelinek 
223989f2807SJerry Jelinek 	/* Wait for IBF = 0 */
224989f2807SJerry Jelinek 	status = kcs_wait_for_ibf(sc, 0);
225989f2807SJerry Jelinek 
226989f2807SJerry Jelinek 	if (KCS_STATUS_STATE(status) != KCS_STATUS_STATE_WRITE)
227989f2807SJerry Jelinek 		return (0);
228989f2807SJerry Jelinek 
229989f2807SJerry Jelinek 	/* Clear OBF */
230989f2807SJerry Jelinek 	kcs_clear_obf(sc, status);
231989f2807SJerry Jelinek 	return (1);
232989f2807SJerry Jelinek }
233989f2807SJerry Jelinek 
234989f2807SJerry Jelinek /*
235989f2807SJerry Jelinek  * Write the last byte of a request message.
236989f2807SJerry Jelinek  */
237989f2807SJerry Jelinek static int
kcs_write_last_byte(struct ipmi_softc * sc,uchar_t data)238989f2807SJerry Jelinek kcs_write_last_byte(struct ipmi_softc *sc, uchar_t data)
239989f2807SJerry Jelinek {
240989f2807SJerry Jelinek 	int status;
241989f2807SJerry Jelinek 
242989f2807SJerry Jelinek 	/* Write end to command */
243989f2807SJerry Jelinek 	OUTB(sc, KCS_CTL_STS, KCS_CONTROL_WRITE_END);
244989f2807SJerry Jelinek 
245989f2807SJerry Jelinek 	/* Wait for IBF = 0 */
246989f2807SJerry Jelinek 	status = kcs_wait_for_ibf(sc, 0);
247989f2807SJerry Jelinek 
248989f2807SJerry Jelinek 	if (KCS_STATUS_STATE(status) != KCS_STATUS_STATE_WRITE)
249989f2807SJerry Jelinek 		/* error state */
250989f2807SJerry Jelinek 		return (0);
251989f2807SJerry Jelinek 
252989f2807SJerry Jelinek 	/* Clear OBF */
253989f2807SJerry Jelinek 	kcs_clear_obf(sc, status);
254989f2807SJerry Jelinek 
255989f2807SJerry Jelinek 	/* Send data byte to DATA. */
256989f2807SJerry Jelinek 	OUTB(sc, KCS_DATA, data);
257989f2807SJerry Jelinek 	return (1);
258989f2807SJerry Jelinek }
259989f2807SJerry Jelinek 
260989f2807SJerry Jelinek /*
261989f2807SJerry Jelinek  * Read one byte of the reply message.
262989f2807SJerry Jelinek  */
263989f2807SJerry Jelinek static int
kcs_read_byte(struct ipmi_softc * sc,uchar_t * data)264989f2807SJerry Jelinek kcs_read_byte(struct ipmi_softc *sc, uchar_t *data)
265989f2807SJerry Jelinek {
266989f2807SJerry Jelinek 	int status;
267989f2807SJerry Jelinek 
268989f2807SJerry Jelinek 	/* Wait for IBF = 0 */
269989f2807SJerry Jelinek 	status = kcs_wait_for_ibf(sc, 0);
270989f2807SJerry Jelinek 
271989f2807SJerry Jelinek 	/* Read State */
272989f2807SJerry Jelinek 	if (KCS_STATUS_STATE(status) == KCS_STATUS_STATE_READ) {
273989f2807SJerry Jelinek 
274989f2807SJerry Jelinek 		/* Wait for OBF = 1 */
275989f2807SJerry Jelinek 		status = kcs_wait_for_obf(sc, 1);
276989f2807SJerry Jelinek 
277989f2807SJerry Jelinek 		/* Read Data_out */
278989f2807SJerry Jelinek 		*data = INB(sc, KCS_DATA);
279989f2807SJerry Jelinek 
280989f2807SJerry Jelinek 		/* Write READ into Data_in */
281989f2807SJerry Jelinek 		OUTB(sc, KCS_DATA, KCS_DATA_IN_READ);
282989f2807SJerry Jelinek 		return (1);
283989f2807SJerry Jelinek 	}
284989f2807SJerry Jelinek 
285989f2807SJerry Jelinek 	/* Idle State */
286989f2807SJerry Jelinek 	if (KCS_STATUS_STATE(status) == KCS_STATUS_STATE_IDLE) {
287989f2807SJerry Jelinek 
288989f2807SJerry Jelinek 		/* Wait for OBF = 1 */
289989f2807SJerry Jelinek 		status = kcs_wait_for_obf(sc, 1);
290989f2807SJerry Jelinek 
291989f2807SJerry Jelinek 		/* Read Dummy */
292989f2807SJerry Jelinek 		(void) INB(sc, KCS_DATA);
293989f2807SJerry Jelinek 		return (2);
294989f2807SJerry Jelinek 	}
295989f2807SJerry Jelinek 
296989f2807SJerry Jelinek 	/* Error State */
297989f2807SJerry Jelinek 	return (0);
298989f2807SJerry Jelinek }
299989f2807SJerry Jelinek 
300989f2807SJerry Jelinek /*
301989f2807SJerry Jelinek  * Send a request message and collect the reply.  Returns true if we
302989f2807SJerry Jelinek  * succeed.
303989f2807SJerry Jelinek  */
304989f2807SJerry Jelinek static int
kcs_polled_request(struct ipmi_softc * sc,struct ipmi_request * req)305989f2807SJerry Jelinek kcs_polled_request(struct ipmi_softc *sc, struct ipmi_request *req)
306989f2807SJerry Jelinek {
307989f2807SJerry Jelinek 	uchar_t *cp, data;
308989f2807SJerry Jelinek 	int i, state;
309989f2807SJerry Jelinek 
310989f2807SJerry Jelinek 	/* Send the request. */
311989f2807SJerry Jelinek 	if (!kcs_start_write(sc)) {
312989f2807SJerry Jelinek 		cmn_err(CE_WARN, "KCS: Failed to start write");
313989f2807SJerry Jelinek 		goto fail;
314989f2807SJerry Jelinek 	}
315989f2807SJerry Jelinek #ifdef KCS_DEBUG
316989f2807SJerry Jelinek 	cmn_err(CE_NOTE, "KCS: WRITE_START... ok");
317989f2807SJerry Jelinek #endif
318989f2807SJerry Jelinek 
319989f2807SJerry Jelinek 	if (!kcs_write_byte(sc, req->ir_addr)) {
320989f2807SJerry Jelinek 		cmn_err(CE_WARN, "KCS: Failed to write address");
321989f2807SJerry Jelinek 		goto fail;
322989f2807SJerry Jelinek 	}
323989f2807SJerry Jelinek #ifdef KCS_DEBUG
324989f2807SJerry Jelinek 	cmn_err(CE_NOTE, "KCS: Wrote address: %02x", req->ir_addr);
325989f2807SJerry Jelinek #endif
326989f2807SJerry Jelinek 
327989f2807SJerry Jelinek 	if (req->ir_requestlen == 0) {
328989f2807SJerry Jelinek 		if (!kcs_write_last_byte(sc, req->ir_command)) {
329989f2807SJerry Jelinek 			cmn_err(CE_WARN,
330989f2807SJerry Jelinek 			    "KCS: Failed to write command");
331989f2807SJerry Jelinek 			goto fail;
332989f2807SJerry Jelinek 		}
333989f2807SJerry Jelinek #ifdef KCS_DEBUG
334989f2807SJerry Jelinek 		cmn_err(CE_NOTE, "KCS: Wrote command: %02x",
335989f2807SJerry Jelinek 		    req->ir_command);
336989f2807SJerry Jelinek #endif
337989f2807SJerry Jelinek 	} else {
338989f2807SJerry Jelinek 		if (!kcs_write_byte(sc, req->ir_command)) {
339989f2807SJerry Jelinek 			cmn_err(CE_WARN,
340989f2807SJerry Jelinek 			    "KCS: Failed to write command");
341989f2807SJerry Jelinek 			goto fail;
342989f2807SJerry Jelinek 		}
343989f2807SJerry Jelinek #ifdef KCS_DEBUG
344989f2807SJerry Jelinek 		cmn_err(CE_NOTE, "KCS: Wrote command: %02x",
345989f2807SJerry Jelinek 		    req->ir_command);
346989f2807SJerry Jelinek #endif
347989f2807SJerry Jelinek 
348989f2807SJerry Jelinek 		cp = req->ir_request;
349989f2807SJerry Jelinek 		for (i = 0; i < req->ir_requestlen - 1; i++) {
350989f2807SJerry Jelinek 			if (!kcs_write_byte(sc, *cp++)) {
351989f2807SJerry Jelinek 				cmn_err(CE_WARN,
352989f2807SJerry Jelinek 				    "KCS: Failed to write data byte %d",
353989f2807SJerry Jelinek 				    i + 1);
354989f2807SJerry Jelinek 				goto fail;
355989f2807SJerry Jelinek 			}
356989f2807SJerry Jelinek #ifdef KCS_DEBUG
357989f2807SJerry Jelinek 			cmn_err(CE_NOTE, "KCS: Wrote data: %02x",
358989f2807SJerry Jelinek 			    cp[-1]);
359989f2807SJerry Jelinek #endif
360989f2807SJerry Jelinek 		}
361989f2807SJerry Jelinek 
362989f2807SJerry Jelinek 		if (!kcs_write_last_byte(sc, *cp)) {
363989f2807SJerry Jelinek 			cmn_err(CE_WARN,
364989f2807SJerry Jelinek 			    "KCS: Failed to write last dta byte");
365989f2807SJerry Jelinek 			goto fail;
366989f2807SJerry Jelinek 		}
367989f2807SJerry Jelinek #ifdef KCS_DEBUG
368989f2807SJerry Jelinek 		cmn_err(CE_NOTE, "KCS: Wrote last data: %02x",
369989f2807SJerry Jelinek 		    *cp);
370989f2807SJerry Jelinek #endif
371989f2807SJerry Jelinek 	}
372989f2807SJerry Jelinek 
373989f2807SJerry Jelinek 	/* Read the reply.  First, read the NetFn/LUN. */
374989f2807SJerry Jelinek 	if (kcs_read_byte(sc, &data) != 1) {
375989f2807SJerry Jelinek 		cmn_err(CE_WARN, "KCS: Failed to read address");
376989f2807SJerry Jelinek 		goto fail;
377989f2807SJerry Jelinek 	}
378989f2807SJerry Jelinek #ifdef KCS_DEBUG
379989f2807SJerry Jelinek 	cmn_err(CE_NOTE, "KCS: Read address: %02x", data);
380989f2807SJerry Jelinek #endif
381989f2807SJerry Jelinek 	if (data != IPMI_REPLY_ADDR(req->ir_addr)) {
382989f2807SJerry Jelinek 		cmn_err(CE_WARN, "KCS: Reply address mismatch");
383989f2807SJerry Jelinek 		goto fail;
384989f2807SJerry Jelinek 	}
385989f2807SJerry Jelinek 
386989f2807SJerry Jelinek 	/* Next we read the command. */
387989f2807SJerry Jelinek 	if (kcs_read_byte(sc, &data) != 1) {
388989f2807SJerry Jelinek 		cmn_err(CE_WARN, "KCS: Failed to read command");
389989f2807SJerry Jelinek 		goto fail;
390989f2807SJerry Jelinek 	}
391989f2807SJerry Jelinek #ifdef KCS_DEBUG
392989f2807SJerry Jelinek 	cmn_err(CE_NOTE, "KCS: Read command: %02x", data);
393989f2807SJerry Jelinek #endif
394989f2807SJerry Jelinek 	if (data != req->ir_command) {
395989f2807SJerry Jelinek 		cmn_err(CE_WARN, "KCS: Command mismatch");
396989f2807SJerry Jelinek 		goto fail;
397989f2807SJerry Jelinek 	}
398989f2807SJerry Jelinek 
399989f2807SJerry Jelinek 	/* Next we read the completion code. */
400989f2807SJerry Jelinek 	if (kcs_read_byte(sc, &req->ir_compcode) != 1) {
401989f2807SJerry Jelinek 		cmn_err(CE_WARN, "KCS: Failed to read completion code");
402989f2807SJerry Jelinek 		goto fail;
403989f2807SJerry Jelinek 	}
404989f2807SJerry Jelinek #ifdef KCS_DEBUG
405989f2807SJerry Jelinek 	cmn_err(CE_NOTE, "KCS: Read completion code: %02x",
406989f2807SJerry Jelinek 	    req->ir_compcode);
407989f2807SJerry Jelinek #endif
408989f2807SJerry Jelinek 
409989f2807SJerry Jelinek 	/* Finally, read the reply from the BMC. */
410989f2807SJerry Jelinek 	i = 0;
411989f2807SJerry Jelinek 	for (;;) {
412989f2807SJerry Jelinek 		state = kcs_read_byte(sc, &data);
413989f2807SJerry Jelinek 		if (state == 0) {
414989f2807SJerry Jelinek 			cmn_err(CE_WARN,
415989f2807SJerry Jelinek 			    "KCS: Read failed on byte %d", i + 1);
416989f2807SJerry Jelinek 			goto fail;
417989f2807SJerry Jelinek 		}
418989f2807SJerry Jelinek 		if (state == 2)
419989f2807SJerry Jelinek 			break;
420989f2807SJerry Jelinek 		if (i < req->ir_replybuflen) {
421989f2807SJerry Jelinek 			req->ir_reply[i] = data;
422989f2807SJerry Jelinek #ifdef KCS_DEBUG
423989f2807SJerry Jelinek 			cmn_err(CE_NOTE, "KCS: Read data %02x",
424989f2807SJerry Jelinek 			    data);
425989f2807SJerry Jelinek 		} else {
426989f2807SJerry Jelinek 			cmn_err(CE_WARN,
427989f2807SJerry Jelinek 			    "KCS: Read short %02x byte %d", data, i + 1);
428989f2807SJerry Jelinek #endif
429989f2807SJerry Jelinek 		}
430989f2807SJerry Jelinek 		i++;
431989f2807SJerry Jelinek 	}
432989f2807SJerry Jelinek 	req->ir_replylen = i;
433989f2807SJerry Jelinek #ifdef KCS_DEBUG
434989f2807SJerry Jelinek 	cmn_err(CE_NOTE, "KCS: READ finished (%d bytes)", i);
435989f2807SJerry Jelinek 	if (req->ir_replybuflen < i)
436989f2807SJerry Jelinek #else
437989f2807SJerry Jelinek 	if (req->ir_replybuflen < i && req->ir_replybuflen != 0)
438989f2807SJerry Jelinek #endif
439989f2807SJerry Jelinek 		cmn_err(CE_WARN, "KCS: Read short: %d buffer, %d actual",
440989f2807SJerry Jelinek 		    (int)(req->ir_replybuflen), i);
441989f2807SJerry Jelinek 	return (1);
442989f2807SJerry Jelinek fail:
443989f2807SJerry Jelinek 	kcs_error(sc);
444989f2807SJerry Jelinek 	return (0);
445989f2807SJerry Jelinek }
446989f2807SJerry Jelinek 
447989f2807SJerry Jelinek static void
kcs_loop(void * arg)448989f2807SJerry Jelinek kcs_loop(void *arg)
449989f2807SJerry Jelinek {
450989f2807SJerry Jelinek 	struct ipmi_softc *sc = arg;
451989f2807SJerry Jelinek 	struct ipmi_request *req;
452989f2807SJerry Jelinek 	int i, ok;
453989f2807SJerry Jelinek 
454989f2807SJerry Jelinek 	IPMI_LOCK(sc);
455989f2807SJerry Jelinek 	while ((req = ipmi_dequeue_request(sc)) != NULL) {
4561e393477SMarcel Telka 		IPMI_UNLOCK(sc);
457989f2807SJerry Jelinek 		ok = 0;
458989f2807SJerry Jelinek 		for (i = 0; i < 3 && !ok; i++)
459989f2807SJerry Jelinek 			ok = kcs_polled_request(sc, req);
460989f2807SJerry Jelinek 		if (ok)
461989f2807SJerry Jelinek 			req->ir_error = 0;
462989f2807SJerry Jelinek 		else
463989f2807SJerry Jelinek 			req->ir_error = EIO;
4641e393477SMarcel Telka 		IPMI_LOCK(sc);
465989f2807SJerry Jelinek 		ipmi_complete_request(sc, req);
466989f2807SJerry Jelinek 	}
467989f2807SJerry Jelinek 	IPMI_UNLOCK(sc);
468989f2807SJerry Jelinek }
469989f2807SJerry Jelinek 
470989f2807SJerry Jelinek static int
kcs_startup(struct ipmi_softc * sc)471989f2807SJerry Jelinek kcs_startup(struct ipmi_softc *sc)
472989f2807SJerry Jelinek {
473989f2807SJerry Jelinek 	sc->ipmi_kthread = taskq_create_proc("ipmi_kcs", 1, minclsyspri, 1, 1,
474989f2807SJerry Jelinek 	    curzone->zone_zsched, TASKQ_PREPOPULATE);
475989f2807SJerry Jelinek 
476989f2807SJerry Jelinek 	if (taskq_dispatch(sc->ipmi_kthread, kcs_loop, (void *) sc,
477*fc8ae2ecSToomas Soome 	    TQ_SLEEP) == TASKQID_INVALID) {
478989f2807SJerry Jelinek 		taskq_destroy(sc->ipmi_kthread);
479989f2807SJerry Jelinek 		return (1);
480989f2807SJerry Jelinek 	}
481989f2807SJerry Jelinek 
482989f2807SJerry Jelinek 	return (0);
483989f2807SJerry Jelinek }
484989f2807SJerry Jelinek 
485989f2807SJerry Jelinek int
ipmi_kcs_attach(struct ipmi_softc * sc)486989f2807SJerry Jelinek ipmi_kcs_attach(struct ipmi_softc *sc)
487989f2807SJerry Jelinek {
488989f2807SJerry Jelinek 	int status;
489989f2807SJerry Jelinek 
490989f2807SJerry Jelinek 	/* Setup function pointers. */
491989f2807SJerry Jelinek 	sc->ipmi_startup = kcs_startup;
492989f2807SJerry Jelinek 	sc->ipmi_enqueue_request = ipmi_polled_enqueue_request;
493989f2807SJerry Jelinek 
494989f2807SJerry Jelinek 	/* See if we can talk to the controller. */
495989f2807SJerry Jelinek 	status = INB(sc, KCS_CTL_STS);
496989f2807SJerry Jelinek 	if (status == 0xff) {
497989f2807SJerry Jelinek 		cmn_err(CE_CONT, "!KCS couldn't find it");
498989f2807SJerry Jelinek 		return (ENXIO);
499989f2807SJerry Jelinek 	}
500989f2807SJerry Jelinek 
501989f2807SJerry Jelinek 	timeout_usecs = drv_hztousec(MAX_TIMEOUT);
502989f2807SJerry Jelinek 
503989f2807SJerry Jelinek #ifdef KCS_DEBUG
504989f2807SJerry Jelinek 	cmn_err(CE_NOTE, "KCS: initial state: %02x", status);
505989f2807SJerry Jelinek #endif
506989f2807SJerry Jelinek 	if (status & KCS_STATUS_OBF ||
507989f2807SJerry Jelinek 	    KCS_STATUS_STATE(status) != KCS_STATUS_STATE_IDLE)
508989f2807SJerry Jelinek 		kcs_error(sc);
509989f2807SJerry Jelinek 
510989f2807SJerry Jelinek 	return (0);
511989f2807SJerry Jelinek }
512