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
49static void	smic_wait_for_tx_okay(struct ipmi_softc *);
50static void	smic_wait_for_rx_okay(struct ipmi_softc *);
51static void	smic_wait_for_not_busy(struct ipmi_softc *);
52static void	smic_set_busy(struct ipmi_softc *);
53
54static void
55smic_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
64static void
65smic_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
74static void
75smic_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
84static void
85smic_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 */
99static int
100smic_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 */
124static int
125smic_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 */
148static int
149smic_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 */
171static int
172smic_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 */
197static int
198smic_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. */
222static int
223smic_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
240static int
241smic_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
355static void
356smic_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
382static int
383smic_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
390static int
391smic_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
408int
409ipmi_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