]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - sys/dev/ipmi/ipmi_smic.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / sys / dev / ipmi / ipmi_smic.c
1 /*-
2  * Copyright (c) 2006 IronPort Systems Inc. <ambrisko@ironport.com>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/bus.h>
33 #include <sys/condvar.h>
34 #include <sys/eventhandler.h>
35 #include <sys/kernel.h>
36 #include <sys/kthread.h>
37 #include <sys/module.h>
38 #include <sys/rman.h>
39 #include <sys/selinfo.h>
40 #include <machine/bus.h>
41
42 #ifdef LOCAL_MODULE
43 #include <ipmi.h>
44 #include <ipmivars.h>
45 #else
46 #include <sys/ipmi.h>
47 #include <dev/ipmi/ipmivars.h>
48 #endif
49
50 static void     smic_wait_for_tx_okay(struct ipmi_softc *);
51 static void     smic_wait_for_rx_okay(struct ipmi_softc *);
52 static void     smic_wait_for_not_busy(struct ipmi_softc *);
53 static void     smic_set_busy(struct ipmi_softc *);
54
55 static void
56 smic_wait_for_tx_okay(struct ipmi_softc *sc)
57 {
58         int flags;
59
60         do {
61                 flags = INB(sc, SMIC_FLAGS);
62         } while (!(flags & SMIC_STATUS_TX_RDY));
63 }
64
65 static void
66 smic_wait_for_rx_okay(struct ipmi_softc *sc)
67 {
68         int flags;
69
70         do {
71                 flags = INB(sc, SMIC_FLAGS);
72         } while (!(flags & SMIC_STATUS_RX_RDY));
73 }
74
75 static void
76 smic_wait_for_not_busy(struct ipmi_softc *sc)
77 {
78         int flags;
79
80         do {
81                 flags = INB(sc, SMIC_FLAGS);
82         } while (flags & SMIC_STATUS_BUSY);
83 }
84
85 static void
86 smic_set_busy(struct ipmi_softc *sc)
87 {
88         int flags;
89
90         flags = INB(sc, SMIC_FLAGS);
91         flags |= SMIC_STATUS_BUSY;
92         flags &= ~SMIC_STATUS_RESERVED;
93         OUTB(sc, SMIC_FLAGS, flags);
94 }
95
96 /*
97  * Start a transfer with a WR_START transaction that sends the NetFn/LUN
98  * address.
99  */
100 static int
101 smic_start_write(struct ipmi_softc *sc, u_char data)
102 {
103         u_char error, status;
104
105         smic_wait_for_not_busy(sc);
106
107         OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_WR_START);
108         OUTB(sc, SMIC_DATA, data);
109         smic_set_busy(sc);
110         smic_wait_for_not_busy(sc);
111         status = INB(sc, SMIC_CTL_STS);
112         if (status != SMIC_SC_SMS_WR_START) {
113                 error = INB(sc, SMIC_DATA);
114                 device_printf(sc->ipmi_dev, "SMIC: Write did not start %02x\n",
115                     error);
116                 return (0);
117         }
118         return (1);
119 }
120
121 /*
122  * Write a byte in the middle of the message (either the command or one of
123  * the data bytes) using a WR_NEXT transaction.
124  */
125 static int
126 smic_write_next(struct ipmi_softc *sc, u_char data)
127 {
128         u_char error, status;
129
130         smic_wait_for_tx_okay(sc);
131         OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_WR_NEXT);
132         OUTB(sc, SMIC_DATA, data);
133         smic_set_busy(sc);
134         smic_wait_for_not_busy(sc);
135         status = INB(sc, SMIC_CTL_STS);
136         if (status != SMIC_SC_SMS_WR_NEXT) {
137                 error = INB(sc, SMIC_DATA);
138                 device_printf(sc->ipmi_dev, "SMIC: Write did not next %02x\n",
139                     error);
140                 return (0);
141         }
142         return (1);
143 }
144
145 /*
146  * Write the last byte of a transfer to end the write phase via a WR_END
147  * transaction.
148  */
149 static int
150 smic_write_last(struct ipmi_softc *sc, u_char data)
151 {
152         u_char error, status;
153
154         smic_wait_for_tx_okay(sc);
155         OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_WR_END);
156         OUTB(sc, SMIC_DATA, data);
157         smic_set_busy(sc);
158         smic_wait_for_not_busy(sc);
159         status = INB(sc, SMIC_CTL_STS);
160         if (status != SMIC_SC_SMS_WR_END) {
161                 error = INB(sc, SMIC_DATA);
162                 device_printf(sc->ipmi_dev, "SMIC: Write did not end %02x\n",
163                     error);
164                 return (0);
165         }
166         return (1);
167 }
168
169 /*
170  * Start the read phase of a transfer with a RD_START transaction.
171  */
172 static int
173 smic_start_read(struct ipmi_softc *sc, u_char *data)
174 {
175         u_char error, status;
176
177         smic_wait_for_not_busy(sc);
178
179         smic_wait_for_rx_okay(sc);
180         OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_RD_START);
181         smic_set_busy(sc);
182         smic_wait_for_not_busy(sc);
183         status = INB(sc, SMIC_CTL_STS);
184         if (status != SMIC_SC_SMS_RD_START) {
185                 error = INB(sc, SMIC_DATA);
186                 device_printf(sc->ipmi_dev, "SMIC: Read did not start %02x\n",
187                     error);
188                 return (0);
189         }
190         *data = INB(sc, SMIC_DATA);
191         return (1);
192 }
193
194 /*
195  * Read a byte via a RD_NEXT transaction.  If this was the last byte, return
196  * 2 rather than 1.
197  */
198 static int
199 smic_read_byte(struct ipmi_softc *sc, u_char *data)
200 {
201         u_char error, status;
202
203         smic_wait_for_rx_okay(sc);
204         OUTB(sc, SMIC_CTL_STS, SMIC_SC_SMS_RD_NEXT);
205         smic_set_busy(sc);
206         smic_wait_for_not_busy(sc);
207         status = INB(sc, SMIC_CTL_STS);
208         if (status != SMIC_SC_SMS_RD_NEXT &&
209             status != SMIC_SC_SMS_RD_END) {
210                 error = INB(sc, SMIC_DATA);
211                 device_printf(sc->ipmi_dev, "SMIC: Read did not next %02x\n",
212                     error);
213                 return (0);
214         }
215         *data = INB(sc, SMIC_DATA);
216         if (status == SMIC_SC_SMS_RD_NEXT)
217                 return (1);
218         else
219                 return (2);
220 }
221
222 /* Complete a transfer via a RD_END transaction after reading the last byte. */
223 static int
224 smic_read_end(struct ipmi_softc *sc)
225 {
226         u_char error, status;
227
228         OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_RD_END);
229         smic_set_busy(sc);
230         smic_wait_for_not_busy(sc);
231         status = INB(sc, SMIC_CTL_STS);
232         if (status != SMIC_SC_SMS_RDY) {
233                 error = INB(sc, SMIC_DATA);
234                 device_printf(sc->ipmi_dev, "SMIC: Read did not end %02x\n",
235                     error);
236                 return (0);
237         }
238         return (1);
239 }
240
241 static int
242 smic_polled_request(struct ipmi_softc *sc, struct ipmi_request *req)
243 {
244         u_char *cp, data;
245         int i, state;
246
247         /* First, start the message with the address. */
248         if (!smic_start_write(sc, req->ir_addr))
249                 return (0);
250 #ifdef SMIC_DEBUG
251         device_printf(sc->ipmi_dev, "SMIC: WRITE_START address: %02x\n",
252             req->ir_addr);
253 #endif
254
255         if (req->ir_requestlen == 0) {
256                 /* Send the command as the last byte. */
257                 if (!smic_write_last(sc, req->ir_command))
258                         return (0);
259 #ifdef SMIC_DEBUG
260                 device_printf(sc->ipmi_dev, "SMIC: Wrote command: %02x\n",
261                     req->ir_command);
262 #endif
263         } else {
264                 /* Send the command. */
265                 if (!smic_write_next(sc, req->ir_command))
266                         return (0);
267 #ifdef SMIC_DEBUG
268                 device_printf(sc->ipmi_dev, "SMIC: Wrote command: %02x\n",
269                     req->ir_command);
270 #endif
271
272                 /* Send the payload. */
273                 cp = req->ir_request;
274                 for (i = 0; i < req->ir_requestlen - 1; i++) {
275                         if (!smic_write_next(sc, *cp++))
276                                 return (0);
277 #ifdef SMIC_DEBUG
278                         device_printf(sc->ipmi_dev, "SMIC: Wrote data: %02x\n",
279                             cp[-1]);
280 #endif
281                 }
282                 if (!smic_write_last(sc, *cp))
283                         return (0);
284 #ifdef SMIC_DEBUG
285                 device_printf(sc->ipmi_dev, "SMIC: Write last data: %02x\n",
286                     *cp);
287 #endif
288         }
289
290         /* Start the read phase by reading the NetFn/LUN. */
291         if (smic_start_read(sc, &data) != 1)
292                 return (0);
293 #ifdef SMIC_DEBUG
294         device_printf(sc->ipmi_dev, "SMIC: Read address: %02x\n", data);
295 #endif
296         if (data != IPMI_REPLY_ADDR(req->ir_addr)) {
297                 device_printf(sc->ipmi_dev, "SMIC: Reply address mismatch\n");
298                 return (0);
299         }
300
301         /* Read the command. */
302         if (smic_read_byte(sc, &data) != 1)
303                 return (0);
304 #ifdef SMIC_DEBUG
305         device_printf(sc->ipmi_dev, "SMIC: Read command: %02x\n", data);
306 #endif
307         if (data != req->ir_command) {
308                 device_printf(sc->ipmi_dev, "SMIC: Command mismatch\n");
309                 return (0);
310         }
311
312         /* Read the completion code. */
313         state = smic_read_byte(sc, &req->ir_compcode);
314         if (state == 0)
315                 return (0);
316 #ifdef SMIC_DEBUG
317         device_printf(sc->ipmi_dev, "SMIC: Read completion code: %02x\n",
318             req->ir_compcode);
319 #endif
320
321         /* Finally, read the reply from the BMC. */
322         i = 0;
323         while (state == 1) {
324                 state = smic_read_byte(sc, &data);
325                 if (state == 0)
326                         return (0);
327                 if (i < req->ir_replybuflen) {
328                         req->ir_reply[i] = data;
329 #ifdef SMIC_DEBUG
330                         device_printf(sc->ipmi_dev, "SMIC: Read data: %02x\n",
331                             data);
332                 } else {
333                         device_printf(sc->ipmi_dev,
334                             "SMIC: Read short %02x byte %d\n", data, i + 1);
335 #endif
336                 }
337                 i++;
338         }
339
340         /* Terminate the transfer. */
341         if (!smic_read_end(sc))
342                 return (0);
343         req->ir_replylen = i;
344 #ifdef SMIC_DEBUG
345         device_printf(sc->ipmi_dev, "SMIC: READ finished (%d bytes)\n", i);
346         if (req->ir_replybuflen < i)
347 #else
348         if (req->ir_replybuflen < i && req->ir_replybuflen != 0)
349 #endif
350                 device_printf(sc->ipmi_dev,
351                     "SMIC: Read short: %zd buffer, %d actual\n",
352                     req->ir_replybuflen, i);
353         return (1);
354 }
355
356 static void
357 smic_loop(void *arg)
358 {
359         struct ipmi_softc *sc = arg;
360         struct ipmi_request *req;
361         int i, ok;
362
363         IPMI_LOCK(sc);
364         while ((req = ipmi_dequeue_request(sc)) != NULL) {
365                 IPMI_UNLOCK(sc);
366                 ok = 0;
367                 for (i = 0; i < 3 && !ok; i++)
368                         ok = smic_polled_request(sc, req);
369                 if (ok)
370                         req->ir_error = 0;
371                 else
372                         req->ir_error = EIO;
373                 IPMI_LOCK(sc);
374                 ipmi_complete_request(sc, req);
375         }
376         IPMI_UNLOCK(sc);
377         kproc_exit(0);
378 }
379
380 static int
381 smic_startup(struct ipmi_softc *sc)
382 {
383
384         return (kproc_create(smic_loop, sc, &sc->ipmi_kthread, 0, 0,
385             "%s: smic", device_get_nameunit(sc->ipmi_dev)));
386 }
387
388 int
389 ipmi_smic_attach(struct ipmi_softc *sc)
390 {
391         int flags;
392
393         /* Setup function pointers. */
394         sc->ipmi_startup = smic_startup;
395         sc->ipmi_enqueue_request = ipmi_polled_enqueue_request;
396
397         /* See if we can talk to the controller. */
398         flags = INB(sc, SMIC_FLAGS);
399         if (flags == 0xff) {
400                 device_printf(sc->ipmi_dev, "couldn't find it\n");
401                 return (ENXIO);
402         }
403
404 #ifdef SMIC_DEBUG
405         device_printf(sc->ipmi_dev, "SMIC: initial state: %02x\n", flags);
406 #endif
407
408         return (0);
409 }