]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/ipmi/ipmi_smic.c
MFV r326785: 8880 improve DTrace error checking
[FreeBSD/FreeBSD.git] / sys / dev / ipmi / ipmi_smic.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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/cdefs.h>
30 __FBSDID("$FreeBSD$");
31
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/bus.h>
35 #include <sys/condvar.h>
36 #include <sys/eventhandler.h>
37 #include <sys/kernel.h>
38 #include <sys/kthread.h>
39 #include <sys/module.h>
40 #include <sys/rman.h>
41 #include <sys/selinfo.h>
42 #include <machine/bus.h>
43
44 #ifdef LOCAL_MODULE
45 #include <ipmi.h>
46 #include <ipmivars.h>
47 #else
48 #include <sys/ipmi.h>
49 #include <dev/ipmi/ipmivars.h>
50 #endif
51
52 static void     smic_wait_for_tx_okay(struct ipmi_softc *);
53 static void     smic_wait_for_rx_okay(struct ipmi_softc *);
54 static void     smic_wait_for_not_busy(struct ipmi_softc *);
55 static void     smic_set_busy(struct ipmi_softc *);
56
57 static void
58 smic_wait_for_tx_okay(struct ipmi_softc *sc)
59 {
60         int flags;
61
62         do {
63                 flags = INB(sc, SMIC_FLAGS);
64         } while (!(flags & SMIC_STATUS_TX_RDY));
65 }
66
67 static void
68 smic_wait_for_rx_okay(struct ipmi_softc *sc)
69 {
70         int flags;
71
72         do {
73                 flags = INB(sc, SMIC_FLAGS);
74         } while (!(flags & SMIC_STATUS_RX_RDY));
75 }
76
77 static void
78 smic_wait_for_not_busy(struct ipmi_softc *sc)
79 {
80         int flags;
81
82         do {
83                 flags = INB(sc, SMIC_FLAGS);
84         } while (flags & SMIC_STATUS_BUSY);
85 }
86
87 static void
88 smic_set_busy(struct ipmi_softc *sc)
89 {
90         int flags;
91
92         flags = INB(sc, SMIC_FLAGS);
93         flags |= SMIC_STATUS_BUSY;
94         flags &= ~SMIC_STATUS_RESERVED;
95         OUTB(sc, SMIC_FLAGS, flags);
96 }
97
98 /*
99  * Start a transfer with a WR_START transaction that sends the NetFn/LUN
100  * address.
101  */
102 static int
103 smic_start_write(struct ipmi_softc *sc, u_char data)
104 {
105         u_char error, status;
106
107         smic_wait_for_not_busy(sc);
108
109         OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_WR_START);
110         OUTB(sc, SMIC_DATA, data);
111         smic_set_busy(sc);
112         smic_wait_for_not_busy(sc);
113         status = INB(sc, SMIC_CTL_STS);
114         if (status != SMIC_SC_SMS_WR_START) {
115                 error = INB(sc, SMIC_DATA);
116                 device_printf(sc->ipmi_dev, "SMIC: Write did not start %02x\n",
117                     error);
118                 return (0);
119         }
120         return (1);
121 }
122
123 /*
124  * Write a byte in the middle of the message (either the command or one of
125  * the data bytes) using a WR_NEXT transaction.
126  */
127 static int
128 smic_write_next(struct ipmi_softc *sc, u_char data)
129 {
130         u_char error, status;
131
132         smic_wait_for_tx_okay(sc);
133         OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_WR_NEXT);
134         OUTB(sc, SMIC_DATA, data);
135         smic_set_busy(sc);
136         smic_wait_for_not_busy(sc);
137         status = INB(sc, SMIC_CTL_STS);
138         if (status != SMIC_SC_SMS_WR_NEXT) {
139                 error = INB(sc, SMIC_DATA);
140                 device_printf(sc->ipmi_dev, "SMIC: Write did not next %02x\n",
141                     error);
142                 return (0);
143         }
144         return (1);
145 }
146
147 /*
148  * Write the last byte of a transfer to end the write phase via a WR_END
149  * transaction.
150  */
151 static int
152 smic_write_last(struct ipmi_softc *sc, u_char data)
153 {
154         u_char error, status;
155
156         smic_wait_for_tx_okay(sc);
157         OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_WR_END);
158         OUTB(sc, SMIC_DATA, data);
159         smic_set_busy(sc);
160         smic_wait_for_not_busy(sc);
161         status = INB(sc, SMIC_CTL_STS);
162         if (status != SMIC_SC_SMS_WR_END) {
163                 error = INB(sc, SMIC_DATA);
164                 device_printf(sc->ipmi_dev, "SMIC: Write did not end %02x\n",
165                     error);
166                 return (0);
167         }
168         return (1);
169 }
170
171 /*
172  * Start the read phase of a transfer with a RD_START transaction.
173  */
174 static int
175 smic_start_read(struct ipmi_softc *sc, u_char *data)
176 {
177         u_char error, status;
178
179         smic_wait_for_not_busy(sc);
180
181         smic_wait_for_rx_okay(sc);
182         OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_RD_START);
183         smic_set_busy(sc);
184         smic_wait_for_not_busy(sc);
185         status = INB(sc, SMIC_CTL_STS);
186         if (status != SMIC_SC_SMS_RD_START) {
187                 error = INB(sc, SMIC_DATA);
188                 device_printf(sc->ipmi_dev, "SMIC: Read did not start %02x\n",
189                     error);
190                 return (0);
191         }
192         *data = INB(sc, SMIC_DATA);
193         return (1);
194 }
195
196 /*
197  * Read a byte via a RD_NEXT transaction.  If this was the last byte, return
198  * 2 rather than 1.
199  */
200 static int
201 smic_read_byte(struct ipmi_softc *sc, u_char *data)
202 {
203         u_char error, status;
204
205         smic_wait_for_rx_okay(sc);
206         OUTB(sc, SMIC_CTL_STS, SMIC_SC_SMS_RD_NEXT);
207         smic_set_busy(sc);
208         smic_wait_for_not_busy(sc);
209         status = INB(sc, SMIC_CTL_STS);
210         if (status != SMIC_SC_SMS_RD_NEXT &&
211             status != SMIC_SC_SMS_RD_END) {
212                 error = INB(sc, SMIC_DATA);
213                 device_printf(sc->ipmi_dev, "SMIC: Read did not next %02x\n",
214                     error);
215                 return (0);
216         }
217         *data = INB(sc, SMIC_DATA);
218         if (status == SMIC_SC_SMS_RD_NEXT)
219                 return (1);
220         else
221                 return (2);
222 }
223
224 /* Complete a transfer via a RD_END transaction after reading the last byte. */
225 static int
226 smic_read_end(struct ipmi_softc *sc)
227 {
228         u_char error, status;
229
230         OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_RD_END);
231         smic_set_busy(sc);
232         smic_wait_for_not_busy(sc);
233         status = INB(sc, SMIC_CTL_STS);
234         if (status != SMIC_SC_SMS_RDY) {
235                 error = INB(sc, SMIC_DATA);
236                 device_printf(sc->ipmi_dev, "SMIC: Read did not end %02x\n",
237                     error);
238                 return (0);
239         }
240         return (1);
241 }
242
243 static int
244 smic_polled_request(struct ipmi_softc *sc, struct ipmi_request *req)
245 {
246         u_char *cp, data;
247         int i, state;
248
249         /* First, start the message with the address. */
250         if (!smic_start_write(sc, req->ir_addr))
251                 return (0);
252 #ifdef SMIC_DEBUG
253         device_printf(sc->ipmi_dev, "SMIC: WRITE_START address: %02x\n",
254             req->ir_addr);
255 #endif
256
257         if (req->ir_requestlen == 0) {
258                 /* Send the command as the last byte. */
259                 if (!smic_write_last(sc, req->ir_command))
260                         return (0);
261 #ifdef SMIC_DEBUG
262                 device_printf(sc->ipmi_dev, "SMIC: Wrote command: %02x\n",
263                     req->ir_command);
264 #endif
265         } else {
266                 /* Send the command. */
267                 if (!smic_write_next(sc, req->ir_command))
268                         return (0);
269 #ifdef SMIC_DEBUG
270                 device_printf(sc->ipmi_dev, "SMIC: Wrote command: %02x\n",
271                     req->ir_command);
272 #endif
273
274                 /* Send the payload. */
275                 cp = req->ir_request;
276                 for (i = 0; i < req->ir_requestlen - 1; i++) {
277                         if (!smic_write_next(sc, *cp++))
278                                 return (0);
279 #ifdef SMIC_DEBUG
280                         device_printf(sc->ipmi_dev, "SMIC: Wrote data: %02x\n",
281                             cp[-1]);
282 #endif
283                 }
284                 if (!smic_write_last(sc, *cp))
285                         return (0);
286 #ifdef SMIC_DEBUG
287                 device_printf(sc->ipmi_dev, "SMIC: Write last data: %02x\n",
288                     *cp);
289 #endif
290         }
291
292         /* Start the read phase by reading the NetFn/LUN. */
293         if (smic_start_read(sc, &data) != 1)
294                 return (0);
295 #ifdef SMIC_DEBUG
296         device_printf(sc->ipmi_dev, "SMIC: Read address: %02x\n", data);
297 #endif
298         if (data != IPMI_REPLY_ADDR(req->ir_addr)) {
299                 device_printf(sc->ipmi_dev, "SMIC: Reply address mismatch\n");
300                 return (0);
301         }
302
303         /* Read the command. */
304         if (smic_read_byte(sc, &data) != 1)
305                 return (0);
306 #ifdef SMIC_DEBUG
307         device_printf(sc->ipmi_dev, "SMIC: Read command: %02x\n", data);
308 #endif
309         if (data != req->ir_command) {
310                 device_printf(sc->ipmi_dev, "SMIC: Command mismatch\n");
311                 return (0);
312         }
313
314         /* Read the completion code. */
315         state = smic_read_byte(sc, &req->ir_compcode);
316         if (state == 0)
317                 return (0);
318 #ifdef SMIC_DEBUG
319         device_printf(sc->ipmi_dev, "SMIC: Read completion code: %02x\n",
320             req->ir_compcode);
321 #endif
322
323         /* Finally, read the reply from the BMC. */
324         i = 0;
325         while (state == 1) {
326                 state = smic_read_byte(sc, &data);
327                 if (state == 0)
328                         return (0);
329                 if (i < req->ir_replybuflen) {
330                         req->ir_reply[i] = data;
331 #ifdef SMIC_DEBUG
332                         device_printf(sc->ipmi_dev, "SMIC: Read data: %02x\n",
333                             data);
334                 } else {
335                         device_printf(sc->ipmi_dev,
336                             "SMIC: Read short %02x byte %d\n", data, i + 1);
337 #endif
338                 }
339                 i++;
340         }
341
342         /* Terminate the transfer. */
343         if (!smic_read_end(sc))
344                 return (0);
345         req->ir_replylen = i;
346 #ifdef SMIC_DEBUG
347         device_printf(sc->ipmi_dev, "SMIC: READ finished (%d bytes)\n", i);
348         if (req->ir_replybuflen < i)
349 #else
350         if (req->ir_replybuflen < i && req->ir_replybuflen != 0)
351 #endif
352                 device_printf(sc->ipmi_dev,
353                     "SMIC: Read short: %zd buffer, %d actual\n",
354                     req->ir_replybuflen, i);
355         return (1);
356 }
357
358 static void
359 smic_loop(void *arg)
360 {
361         struct ipmi_softc *sc = arg;
362         struct ipmi_request *req;
363         int i, ok;
364
365         IPMI_LOCK(sc);
366         while ((req = ipmi_dequeue_request(sc)) != NULL) {
367                 IPMI_UNLOCK(sc);
368                 ok = 0;
369                 for (i = 0; i < 3 && !ok; i++) {
370                         IPMI_IO_LOCK(sc);
371                         ok = smic_polled_request(sc, req);
372                         IPMI_IO_UNLOCK(sc);
373                 }
374                 if (ok)
375                         req->ir_error = 0;
376                 else
377                         req->ir_error = EIO;
378                 IPMI_LOCK(sc);
379                 ipmi_complete_request(sc, req);
380         }
381         IPMI_UNLOCK(sc);
382         kproc_exit(0);
383 }
384
385 static int
386 smic_startup(struct ipmi_softc *sc)
387 {
388
389         return (kproc_create(smic_loop, sc, &sc->ipmi_kthread, 0, 0,
390             "%s: smic", device_get_nameunit(sc->ipmi_dev)));
391 }
392
393 static int
394 smic_driver_request(struct ipmi_softc *sc, struct ipmi_request *req, int timo)
395 {
396         int i, ok;
397
398         ok = 0;
399         for (i = 0; i < 3 && !ok; i++) {
400                 IPMI_IO_LOCK(sc);
401                 ok = smic_polled_request(sc, req);
402                 IPMI_IO_UNLOCK(sc);
403         }
404         if (ok)
405                 req->ir_error = 0;
406         else
407                 req->ir_error = EIO;
408         return (req->ir_error);
409 }
410
411 int
412 ipmi_smic_attach(struct ipmi_softc *sc)
413 {
414         int flags;
415
416         /* Setup function pointers. */
417         sc->ipmi_startup = smic_startup;
418         sc->ipmi_enqueue_request = ipmi_polled_enqueue_request;
419         sc->ipmi_driver_request = smic_driver_request;
420         sc->ipmi_driver_requests_polled = 1;
421
422         /* See if we can talk to the controller. */
423         flags = INB(sc, SMIC_FLAGS);
424         if (flags == 0xff) {
425                 device_printf(sc->ipmi_dev, "couldn't find it\n");
426                 return (ENXIO);
427         }
428
429 #ifdef SMIC_DEBUG
430         device_printf(sc->ipmi_dev, "SMIC: initial state: %02x\n", flags);
431 #endif
432
433         return (0);
434 }