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  * 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 }

Cache object: f9df0c8c017fa0f2402a4651b1a620d3


[ 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.