2 * Copyright (C) 2018 Justin Hibbits
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
17 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
18 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
19 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
20 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
21 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
22 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 #include <sys/cdefs.h>
26 __FBSDID("$FreeBSD$");
28 #include <sys/param.h>
29 #include <sys/kernel.h>
30 #include <sys/systm.h>
32 #include <sys/module.h>
33 #include <sys/mutex.h>
35 #include <sys/kthread.h>
37 #include <sys/selinfo.h>
38 #include <sys/sysctl.h>
43 #include <machine/bus.h>
45 #include <dev/ofw/openfirm.h>
46 #include <dev/ofw/ofw_bus.h>
47 #include <dev/ofw/ofw_bus_subr.h>
50 #include <dev/ipmi/ipmivars.h>
52 #include <powerpc/powernv/opal.h>
58 * 1 - enable error messages (EPRINTF)
59 * 2 - enable error and debug messages (DPRINTF)
61 #define OPAL_IPMI_DEBUG 0
62 #if OPAL_IPMI_DEBUG >= 2
64 #define DPRINTF(fmt, ...) printf("ipmi: " fmt "\n", ## __VA_ARGS__)
66 #define DPRINTF(fmt, ...) ((void)0)
68 #if OPAL_IPMI_DEBUG >= 1
69 /* error printf: to print messages only when something fails */
70 #define EPRINTF(fmt, ...) printf("ipmi: " fmt "\n", ## __VA_ARGS__)
72 #define EPRINTF(fmt, ...) ((void)0)
75 struct opal_ipmi_softc {
76 struct ipmi_softc ipmi;
77 uint64_t sc_interface;
79 struct opal_ipmi_msg *sc_msg; /* Protected by IPMI lock */
82 static MALLOC_DEFINE(M_IPMI, "ipmi", "OPAL IPMI");
85 opal_ipmi_recv(struct opal_ipmi_softc *sc, uint64_t *msg_len, int timo)
91 timo *= 10; /* Timeout is in milliseconds, we delay in 100us */
94 *msg_len = sizeof(struct opal_ipmi_msg) + IPMI_MAX_RX;
95 /* Crank the OPAL state machine while we poll for a reply. */
96 opal_call(OPAL_POLL_EVENTS, NULL);
97 err = opal_call(OPAL_IPMI_RECV, sc->sc_interface,
98 vtophys(sc->sc_msg), vtophys(msg_len));
99 if (err != OPAL_EMPTY)
109 if (err != OPAL_SUCCESS)
110 EPRINTF("RECV: error: %d", err);
114 DPRINTF("RECV: rv=%02x len=%ld",
115 sc->sc_msg->data[0], *msg_len);
127 opal_ipmi_discard_msgs(struct opal_ipmi_softc *sc)
132 /* OPAL_IPMI_RECV fails when msg version is not set. */
133 sc->sc_msg->version = OPAL_IPMI_MSG_FORMAT_VERSION_1;
135 /* Wait up to 100ms for the 1st timedout message. */
136 err = opal_ipmi_recv(sc, &msg_len, 100);
139 /* Wait only 10ms for the remaining messages. */
140 err = opal_ipmi_recv(sc, &msg_len, 10);
143 EPRINTF("Discarded %d message(s)", i);
148 opal_ipmi_polled_request(struct opal_ipmi_softc *sc, struct ipmi_request *req,
155 * Discard timed out messages before sending a new one, to avoid
156 * them being confused with the reply of the new message.
159 opal_ipmi_discard_msgs(sc);
161 /* Construct and send the message. */
162 sc->sc_msg->version = OPAL_IPMI_MSG_FORMAT_VERSION_1;
163 sc->sc_msg->netfn = req->ir_addr;
164 sc->sc_msg->cmd = req->ir_command;
166 if (req->ir_requestlen > IPMI_MAX_RX) {
170 memcpy(sc->sc_msg->data, req->ir_request, req->ir_requestlen);
172 msg_len = sizeof(*sc->sc_msg) + req->ir_requestlen;
173 err = opal_call(OPAL_IPMI_SEND, sc->sc_interface, vtophys(sc->sc_msg),
176 DPRINTF("SEND: cmd=%02x netfn=%02x len=%ld -> %d",
177 sc->sc_msg->cmd, sc->sc_msg->netfn, msg_len, err);
179 if (err != OPAL_SUCCESS)
180 EPRINTF("SEND: error: %d", err);
186 case OPAL_UNSUPPORTED:
198 if ((err = opal_ipmi_recv(sc, &msg_len, timo)) == 0) {
199 /* Subtract one extra for the completion code. */
200 req->ir_replylen = msg_len - sizeof(struct opal_ipmi_msg) - 1;
201 req->ir_replylen = min(req->ir_replylen, req->ir_replybuflen);
202 memcpy(req->ir_reply, &sc->sc_msg->data[1], req->ir_replylen);
203 req->ir_compcode = sc->sc_msg->data[0];
211 opal_ipmi_probe(device_t dev)
213 if (!ofw_bus_is_compatible(dev, "ibm,opal-ipmi"))
216 device_set_desc(dev, "OPAL IPMI System Interface");
218 return (BUS_PROBE_DEFAULT);
222 opal_ipmi_loop(void *arg)
224 struct opal_ipmi_softc *sc = arg;
225 struct ipmi_request *req;
228 IPMI_LOCK(&sc->ipmi);
229 while ((req = ipmi_dequeue_request(&sc->ipmi)) != NULL) {
230 IPMI_UNLOCK(&sc->ipmi);
232 for (i = 0; i < 3 && err != 0; i++) {
233 IPMI_IO_LOCK(&sc->ipmi);
234 err = opal_ipmi_polled_request(sc, req, MAX_TIMEOUT);
235 IPMI_IO_UNLOCK(&sc->ipmi);
237 req->ir_error = err == 0 ? 0 : EIO;
238 IPMI_LOCK(&sc->ipmi);
239 ipmi_complete_request(&sc->ipmi, req);
241 IPMI_UNLOCK(&sc->ipmi);
246 opal_ipmi_startup(struct ipmi_softc *sc)
249 return (kproc_create(opal_ipmi_loop, sc, &sc->ipmi_kthread, 0, 0,
250 "%s: opal", device_get_nameunit(sc->ipmi_dev)));
254 opal_ipmi_driver_request(struct ipmi_softc *isc, struct ipmi_request *req,
257 struct opal_ipmi_softc *sc = (struct opal_ipmi_softc *)isc;
260 for (i = 0; i < 3; i++) {
261 IPMI_LOCK(&sc->ipmi);
262 err = opal_ipmi_polled_request(sc, req, timo);
263 IPMI_UNLOCK(&sc->ipmi);
274 opal_ipmi_attach(device_t dev)
276 struct opal_ipmi_softc *sc;
279 sc = device_get_softc(dev);
281 if (OF_getencprop(ofw_bus_get_node(dev), "ibm,ipmi-interface-id",
282 &ifid, sizeof(ifid)) < 0) {
283 device_printf(dev, "Missing interface id\n");
286 sc->sc_interface = ifid;
287 sc->ipmi.ipmi_startup = opal_ipmi_startup;
288 sc->ipmi.ipmi_driver_request = opal_ipmi_driver_request;
289 sc->ipmi.ipmi_enqueue_request = ipmi_polled_enqueue_request;
290 sc->ipmi.ipmi_driver_requests_polled = 1;
291 sc->ipmi.ipmi_dev = dev;
293 sc->sc_msg = malloc(sizeof(struct opal_ipmi_msg) + IPMI_MAX_RX, M_IPMI,
296 /* Discard old messages that may have remained in receive queue. */
297 opal_ipmi_discard_msgs(sc);
299 return (ipmi_attach(dev));
303 opal_ipmi_detach(device_t dev)
305 struct opal_ipmi_softc *sc;
308 sc = device_get_softc(dev);
309 err = ipmi_detach(dev);
311 free(sc->sc_msg, M_IPMI);
316 static device_method_t opal_ipmi_methods[] = {
317 /* Device interface */
318 DEVMETHOD(device_probe, opal_ipmi_probe),
319 DEVMETHOD(device_attach, opal_ipmi_attach),
320 DEVMETHOD(device_detach, opal_ipmi_detach),
324 static driver_t opal_ipmi_driver = {
327 sizeof(struct opal_ipmi_softc)
330 DRIVER_MODULE(opal_ipmi, opal, opal_ipmi_driver, ipmi_devclass, NULL, NULL);