The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/ipmi/ipmi_smic.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    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: releng/8.4/sys/dev/ipmi/ipmi_smic.c 172836 2007-10-20 23:23:23Z julian $");
   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                 ok = 0;
  366                 for (i = 0; i < 3 && !ok; i++)
  367                         ok = smic_polled_request(sc, req);
  368                 if (ok)
  369                         req->ir_error = 0;
  370                 else
  371                         req->ir_error = EIO;
  372                 ipmi_complete_request(sc, req);
  373         }
  374         IPMI_UNLOCK(sc);
  375         kproc_exit(0);
  376 }
  377 
  378 static int
  379 smic_startup(struct ipmi_softc *sc)
  380 {
  381 
  382         return (kproc_create(smic_loop, sc, &sc->ipmi_kthread, 0, 0,
  383             "%s: smic", device_get_nameunit(sc->ipmi_dev)));
  384 }
  385 
  386 int
  387 ipmi_smic_attach(struct ipmi_softc *sc)
  388 {
  389         int flags;
  390 
  391         /* Setup function pointers. */
  392         sc->ipmi_startup = smic_startup;
  393         sc->ipmi_enqueue_request = ipmi_polled_enqueue_request;
  394 
  395         /* See if we can talk to the controller. */
  396         flags = INB(sc, SMIC_FLAGS);
  397         if (flags == 0xff) {
  398                 device_printf(sc->ipmi_dev, "couldn't find it\n");
  399                 return (ENXIO);
  400         }
  401 
  402 #ifdef SMIC_DEBUG
  403         device_printf(sc->ipmi_dev, "SMIC: initial state: %02x\n", flags);
  404 #endif
  405 
  406         return (0);
  407 }

Cache object: 8750a2c481b5824a9229e5ed6af6c5c3


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.