freebsd-src/sys/dev/ipmi/ipmi_bt.c
Warner Losh fdafd315ad sys: Automated cleanup of cdefs and other formatting
Apply the following automated changes to try to eliminate
no-longer-needed sys/cdefs.h includes as well as now-empty
blank lines in a row.

Remove /^#if.*\n#endif.*\n#include\s+<sys/cdefs.h>.*\n/
Remove /\n+#include\s+<sys/cdefs.h>.*\n+#if.*\n#endif.*\n+/
Remove /\n+#if.*\n#endif.*\n+/
Remove /^#if.*\n#endif.*\n/
Remove /\n+#include\s+<sys/cdefs.h>\n#include\s+<sys/types.h>/
Remove /\n+#include\s+<sys/cdefs.h>\n#include\s+<sys/param.h>/
Remove /\n+#include\s+<sys/cdefs.h>\n#include\s+<sys/capsicum.h>/

Sponsored by:		Netflix
2023-11-26 22:24:00 -07:00

294 lines
7.7 KiB
C

/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2023 Yandex LLC
* Copyright (c) 2023 Andrey V. Elsukov <ae@FreeBSD.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/condvar.h>
#include <sys/eventhandler.h>
#include <sys/kernel.h>
#include <sys/kthread.h>
#include <sys/module.h>
#include <sys/rman.h>
#include <sys/selinfo.h>
#include <machine/bus.h>
#include <sys/ipmi.h>
#include <dev/ipmi/ipmivars.h>
/*
* BT interface
*/
#define DMSG0(sc, fmt, ...) do { \
device_printf((sc)->ipmi_dev, "BT: %s: " fmt "\n", \
__func__, ## __VA_ARGS__); \
} while (0)
#define DMSGV(...) if (bootverbose) { \
DMSG0(__VA_ARGS__); \
}
#ifdef IPMI_BT_DEBUG
#define DMSG(...) DMSG0(__VA_ARGS__)
#else
#define DMSG(...)
#endif
#define BT_IO_BASE 0xe4
#define BT_CTRL_REG 0
#define BT_C_CLR_WR_PTR (1L << 0)
#define BT_C_CLR_RD_PTR (1L << 1)
#define BT_C_H2B_ATN (1L << 2)
#define BT_C_B2H_ATN (1L << 3)
#define BT_C_SMS_ATN (1L << 4)
#define BT_C_OEM0 (1L << 5)
#define BT_C_H_BUSY (1L << 6)
#define BT_C_B_BUSY (1L << 7)
#define BT_CTRL_BITS "\20\01CLR_WR_PTR\02CLR_RD_PTR\03H2B_ATN\04B2H_ATN"\
"\05SMS_ATN\06OEM0\07H_BUSY\010B_BUSY"
#define BT_DATA_REG 1
#define BTMSG_REQLEN 3
#define BTMSG_REPLEN 4
#define BT_INTMASK_REG 2
#define BT_IM_B2H_IRQ_EN (1L << 0)
#define BT_IM_B2H_IRQ (1L << 1)
#define BT_IM_BMC_HWRST (1L << 7)
static int bt_polled_request(struct ipmi_softc *, struct ipmi_request *);
static int bt_driver_request(struct ipmi_softc *, struct ipmi_request *, int);
static int bt_wait(struct ipmi_softc *, uint8_t, uint8_t);
static int bt_reset(struct ipmi_softc *);
static void bt_loop(void *);
static int bt_startup(struct ipmi_softc *);
#define BT_DELAY_MIN 1
#define BT_DELAY_MAX 256
static int
bt_wait(struct ipmi_softc *sc, uint8_t mask, uint8_t wanted)
{
volatile uint8_t value;
int delay = BT_DELAY_MIN;
int count = 20000; /* about 5 seconds */
while (count--) {
value = INB(sc, BT_CTRL_REG);
if ((value & mask) == wanted)
return (value);
/*
* The wait delay is increased exponentially to avoid putting
* significant load on I/O bus.
*/
DELAY(delay);
if (delay < BT_DELAY_MAX)
delay <<= 1;
}
DMSGV(sc, "failed: m=%b w=%b v=0x%02x\n",
mask, BT_CTRL_BITS, wanted, BT_CTRL_BITS, value);
return (-1);
}
static int
bt_reset(struct ipmi_softc *sc)
{
uint8_t v;
v = INB(sc, BT_CTRL_REG);
DMSG(sc, "ctrl: %b", v, BT_CTRL_BITS);
v &= BT_C_H_BUSY; /* clear H_BUSY iff it set */
v |= BT_C_CLR_WR_PTR | BT_C_CLR_RD_PTR | BT_C_B2H_ATN | BT_C_H2B_ATN;
bt_wait(sc, BT_C_B_BUSY, 0);
OUTB(sc, BT_CTRL_REG, v);
v = BT_IM_B2H_IRQ | BT_IM_BMC_HWRST;
OUTB(sc, BT_INTMASK_REG, v);
return (0);
}
/*
* Send a request message and collect the reply. Returns 1 if we
* succeed.
*/
static int
bt_polled_request(struct ipmi_softc *sc, struct ipmi_request *req)
{
uint8_t addr, cmd, seq, v;
int i;
IPMI_IO_LOCK(sc);
/*
* Send the request:
*
* Byte 1 | Byte 2 | Byte 3 | Byte 4 | Byte 5:N
* -------+-----------+--------+--------+---------
* Length | NetFn/LUN | Seq | Cmd | Data
*/
if (bt_wait(sc, BT_C_B_BUSY | BT_C_H2B_ATN, 0) < 0) {
DMSG(sc, "failed to start write transfer");
goto fail;
}
DMSG(sc, "request: length=%d, addr=0x%02x, seq=%u, cmd=0x%02x",
(int)req->ir_requestlen, req->ir_addr, sc->ipmi_bt_seq, req->ir_command);
OUTB(sc, BT_CTRL_REG, BT_C_CLR_WR_PTR);
OUTB(sc, BT_DATA_REG, req->ir_requestlen + BTMSG_REQLEN);
OUTB(sc, BT_DATA_REG, req->ir_addr);
OUTB(sc, BT_DATA_REG, sc->ipmi_bt_seq);
OUTB(sc, BT_DATA_REG, req->ir_command);
for (i = 0; i < req->ir_requestlen; i++)
OUTB(sc, BT_DATA_REG, req->ir_request[i]);
OUTB(sc, BT_CTRL_REG, BT_C_H2B_ATN);
if (bt_wait(sc, BT_C_B_BUSY | BT_C_H2B_ATN, 0) < 0) {
DMSG(sc, "failed to finish write transfer");
goto fail;
}
/*
* Read the reply:
*
* Byte 1 | Byte 2 | Byte 3 | Byte 4 | Byte 5 | Byte 6:N
* -------+-----------+--------+--------+-----------------+---------
* Length | NetFn/LUN | Seq | Cmd | Completion code | Data
*/
if (bt_wait(sc, BT_C_B2H_ATN, BT_C_B2H_ATN) < 0) {
DMSG(sc, "got no reply from BMC");
goto fail;
}
OUTB(sc, BT_CTRL_REG, BT_C_H_BUSY);
OUTB(sc, BT_CTRL_REG, BT_C_B2H_ATN);
OUTB(sc, BT_CTRL_REG, BT_C_CLR_RD_PTR);
i = INB(sc, BT_DATA_REG);
if (i < BTMSG_REPLEN) {
DMSG(sc, "wrong data length: %d", i);
goto fail;
}
req->ir_replylen = i - BTMSG_REPLEN;
DMSG(sc, "data length: %d, frame length: %d", req->ir_replylen, i);
addr = INB(sc, BT_DATA_REG);
if (addr != IPMI_REPLY_ADDR(req->ir_addr)) {
DMSGV(sc, "address doesn't match: addr=0x%02x vs. 0x%02x",
req->ir_addr, addr);
}
seq = INB(sc, BT_DATA_REG);
if (seq != sc->ipmi_bt_seq) {
DMSGV(sc, "seq number doesn't match: seq=0x%02x vs. 0x%02x",
sc->ipmi_bt_seq, seq);
}
cmd = INB(sc, BT_DATA_REG);
if (cmd != req->ir_command) {
DMSGV(sc, "command doesn't match: cmd=0x%02x vs. 0x%02x",
req->ir_command, cmd);
}
req->ir_compcode = INB(sc, BT_DATA_REG);
for (i = 0; i < req->ir_replylen; i++) {
v = INB(sc, BT_DATA_REG);
if (i < req->ir_replybuflen)
req->ir_reply[i] = v;
}
OUTB(sc, BT_CTRL_REG, BT_C_H_BUSY);
IPMI_IO_UNLOCK(sc);
DMSG(sc, "reply: length=%d, addr=0x%02x, seq=%u, cmd=0x%02x, code=0x%02x",
(int)req->ir_replylen, addr, seq, req->ir_command, req->ir_compcode);
return (1);
fail:
bt_reset(sc);
IPMI_IO_UNLOCK(sc);
return (0);
}
static void
bt_loop(void *arg)
{
struct ipmi_softc *sc = arg;
struct ipmi_request *req;
IPMI_LOCK(sc);
while ((req = ipmi_dequeue_request(sc)) != NULL) {
IPMI_UNLOCK(sc);
(void)bt_driver_request(sc, req, 0);
IPMI_LOCK(sc);
sc->ipmi_bt_seq++;
ipmi_complete_request(sc, req);
}
IPMI_UNLOCK(sc);
kproc_exit(0);
}
static int
bt_startup(struct ipmi_softc *sc)
{
return (kproc_create(bt_loop, sc, &sc->ipmi_kthread, 0, 0, "%s: bt",
device_get_nameunit(sc->ipmi_dev)));
}
static int
bt_driver_request(struct ipmi_softc *sc, struct ipmi_request *req, int timo __unused)
{
int i, ok;
ok = 0;
for (i = 0; i < 3 && !ok; i++)
ok = bt_polled_request(sc, req);
if (ok)
req->ir_error = 0;
else
req->ir_error = EIO;
return (req->ir_error);
}
int
ipmi_bt_attach(struct ipmi_softc *sc)
{
/* Setup function pointers. */
sc->ipmi_startup = bt_startup;
sc->ipmi_enqueue_request = ipmi_polled_enqueue_request;
sc->ipmi_driver_request = bt_driver_request;
sc->ipmi_driver_requests_polled = 1;
sc->ipmi_bt_seq = 1;
return (bt_reset(sc));
}