1*aa194c9cSriastradh /* $NetBSD: ipmi.c,v 1.14 2024/12/04 15:26:07 riastradh Exp $ */ 2dc766f2bSmlelstv 3dc766f2bSmlelstv /* 4b1f23fc9Smlelstv * Copyright (c) 2019 Michael van Elst 5b1f23fc9Smlelstv * 6b1f23fc9Smlelstv * Redistribution and use in source and binary forms, with or without 7b1f23fc9Smlelstv * modification, are permitted provided that the following conditions 8b1f23fc9Smlelstv * are met: 9b1f23fc9Smlelstv * 1. Redistributions of source code must retain the above copyright 10b1f23fc9Smlelstv * notice, this list of conditions and the following disclaimer. 11b1f23fc9Smlelstv * 2. Redistributions in binary form must reproduce the above copyright 12b1f23fc9Smlelstv * notice, this list of conditions and the following disclaimer in the 13b1f23fc9Smlelstv * documentation and/or other materials provided with the distribution. 14b1f23fc9Smlelstv * 15b1f23fc9Smlelstv * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16b1f23fc9Smlelstv * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17b1f23fc9Smlelstv * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18b1f23fc9Smlelstv * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19b1f23fc9Smlelstv * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20b1f23fc9Smlelstv * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21b1f23fc9Smlelstv * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22b1f23fc9Smlelstv * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23b1f23fc9Smlelstv * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24b1f23fc9Smlelstv * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25b1f23fc9Smlelstv * 26b1f23fc9Smlelstv */ 27b1f23fc9Smlelstv /* 28dc766f2bSmlelstv * Copyright (c) 2006 Manuel Bouyer. 29dc766f2bSmlelstv * 30dc766f2bSmlelstv * Redistribution and use in source and binary forms, with or without 31dc766f2bSmlelstv * modification, are permitted provided that the following conditions 32dc766f2bSmlelstv * are met: 33dc766f2bSmlelstv * 1. Redistributions of source code must retain the above copyright 34dc766f2bSmlelstv * notice, this list of conditions and the following disclaimer. 35dc766f2bSmlelstv * 2. Redistributions in binary form must reproduce the above copyright 36dc766f2bSmlelstv * notice, this list of conditions and the following disclaimer in the 37dc766f2bSmlelstv * documentation and/or other materials provided with the distribution. 38dc766f2bSmlelstv * 39dc766f2bSmlelstv * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 40dc766f2bSmlelstv * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 41dc766f2bSmlelstv * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 42dc766f2bSmlelstv * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 43dc766f2bSmlelstv * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 44dc766f2bSmlelstv * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 45dc766f2bSmlelstv * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 46dc766f2bSmlelstv * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 47dc766f2bSmlelstv * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 48dc766f2bSmlelstv * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 49dc766f2bSmlelstv * 50dc766f2bSmlelstv */ 51dc766f2bSmlelstv 52dc766f2bSmlelstv /* 53dc766f2bSmlelstv * Copyright (c) 2005 Jordan Hargrave 54dc766f2bSmlelstv * All rights reserved. 55dc766f2bSmlelstv * 56dc766f2bSmlelstv * Redistribution and use in source and binary forms, with or without 57dc766f2bSmlelstv * modification, are permitted provided that the following conditions 58dc766f2bSmlelstv * are met: 59dc766f2bSmlelstv * 1. Redistributions of source code must retain the above copyright 60dc766f2bSmlelstv * notice, this list of conditions and the following disclaimer. 61dc766f2bSmlelstv * 2. Redistributions in binary form must reproduce the above copyright 62dc766f2bSmlelstv * notice, this list of conditions and the following disclaimer in the 63dc766f2bSmlelstv * documentation and/or other materials provided with the distribution. 64dc766f2bSmlelstv * 65dc766f2bSmlelstv * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 66dc766f2bSmlelstv * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 67dc766f2bSmlelstv * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 68dc766f2bSmlelstv * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR 69dc766f2bSmlelstv * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 70dc766f2bSmlelstv * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 71dc766f2bSmlelstv * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 72dc766f2bSmlelstv * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 73dc766f2bSmlelstv * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 74dc766f2bSmlelstv * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 75dc766f2bSmlelstv * SUCH DAMAGE. 76dc766f2bSmlelstv */ 77dc766f2bSmlelstv 78dc766f2bSmlelstv #include <sys/cdefs.h> 79*aa194c9cSriastradh __KERNEL_RCSID(0, "$NetBSD: ipmi.c,v 1.14 2024/12/04 15:26:07 riastradh Exp $"); 80dc766f2bSmlelstv 81dc766f2bSmlelstv #include <sys/types.h> 82dc766f2bSmlelstv #include <sys/param.h> 83dc766f2bSmlelstv #include <sys/systm.h> 84dc766f2bSmlelstv #include <sys/kernel.h> 85dc766f2bSmlelstv #include <sys/device.h> 86dc766f2bSmlelstv #include <sys/extent.h> 87dc766f2bSmlelstv #include <sys/callout.h> 88dc766f2bSmlelstv #include <sys/envsys.h> 89dc766f2bSmlelstv #include <sys/malloc.h> 90dc766f2bSmlelstv #include <sys/kthread.h> 91dc766f2bSmlelstv #include <sys/bus.h> 92dc766f2bSmlelstv #include <sys/intr.h> 93b1f23fc9Smlelstv #include <sys/ioctl.h> 94b1f23fc9Smlelstv #include <sys/poll.h> 95b1f23fc9Smlelstv #include <sys/conf.h> 96dc766f2bSmlelstv 97dc766f2bSmlelstv #include <dev/isa/isareg.h> 98dc766f2bSmlelstv #include <dev/isa/isavar.h> 99dc766f2bSmlelstv 100b1f23fc9Smlelstv #include <sys/ipmi.h> 101dc766f2bSmlelstv #include <dev/ipmivar.h> 102dc766f2bSmlelstv 103dc766f2bSmlelstv #include <uvm/uvm_extern.h> 104dc766f2bSmlelstv 105b1f23fc9Smlelstv #include "ioconf.h" 106b1f23fc9Smlelstv 107b1f23fc9Smlelstv static dev_type_open(ipmi_open); 108b1f23fc9Smlelstv static dev_type_close(ipmi_close); 109b1f23fc9Smlelstv static dev_type_ioctl(ipmi_ioctl); 110b1f23fc9Smlelstv static dev_type_poll(ipmi_poll); 111b1f23fc9Smlelstv 112b1f23fc9Smlelstv const struct cdevsw ipmi_cdevsw = { 113b1f23fc9Smlelstv .d_open = ipmi_open, 114b1f23fc9Smlelstv .d_close = ipmi_close, 115b1f23fc9Smlelstv .d_read = noread, 116b1f23fc9Smlelstv .d_write = nowrite, 117b1f23fc9Smlelstv .d_ioctl = ipmi_ioctl, 118b1f23fc9Smlelstv .d_stop = nostop, 119b1f23fc9Smlelstv .d_tty = notty, 120b1f23fc9Smlelstv .d_poll = ipmi_poll, 121b1f23fc9Smlelstv .d_mmap = nommap, 122b1f23fc9Smlelstv .d_kqfilter = nokqfilter, 123b1f23fc9Smlelstv .d_discard = nodiscard, 124b1f23fc9Smlelstv .d_flag = D_OTHER 125b1f23fc9Smlelstv }; 126b1f23fc9Smlelstv 127b1f23fc9Smlelstv #define IPMIUNIT(n) (minor(n)) 128b1f23fc9Smlelstv 129dc766f2bSmlelstv struct ipmi_sensor { 130dc766f2bSmlelstv uint8_t *i_sdr; 131dc766f2bSmlelstv int i_num; 132dc766f2bSmlelstv int i_stype; 133dc766f2bSmlelstv int i_etype; 134dc766f2bSmlelstv char i_envdesc[64]; 135dc766f2bSmlelstv int i_envtype; /* envsys compatible type */ 136dc766f2bSmlelstv int i_envnum; /* envsys index */ 137dc766f2bSmlelstv sysmon_envsys_lim_t i_limits, i_deflims; 138dc766f2bSmlelstv uint32_t i_props, i_defprops; 139dc766f2bSmlelstv SLIST_ENTRY(ipmi_sensor) i_list; 140dc766f2bSmlelstv int32_t i_prevval; /* feed rnd source on change */ 141dc766f2bSmlelstv }; 142dc766f2bSmlelstv 143dc766f2bSmlelstv #if 0 144dc766f2bSmlelstv static int ipmi_nintr; 145dc766f2bSmlelstv #endif 146dc766f2bSmlelstv static int ipmi_dbg = 0; 147dc766f2bSmlelstv static int ipmi_enabled = 0; 148dc766f2bSmlelstv 149dc766f2bSmlelstv #define SENSOR_REFRESH_RATE (hz / 2) 150dc766f2bSmlelstv 151dc766f2bSmlelstv #define IPMI_BTMSG_LEN 0 152dc766f2bSmlelstv #define IPMI_BTMSG_NFLN 1 153dc766f2bSmlelstv #define IPMI_BTMSG_SEQ 2 154dc766f2bSmlelstv #define IPMI_BTMSG_CMD 3 155dc766f2bSmlelstv #define IPMI_BTMSG_CCODE 4 156dc766f2bSmlelstv #define IPMI_BTMSG_DATASND 4 157dc766f2bSmlelstv #define IPMI_BTMSG_DATARCV 5 158dc766f2bSmlelstv 159dc766f2bSmlelstv #define IPMI_MSG_NFLN 0 160dc766f2bSmlelstv #define IPMI_MSG_CMD 1 161dc766f2bSmlelstv #define IPMI_MSG_CCODE 2 162dc766f2bSmlelstv #define IPMI_MSG_DATASND 2 163dc766f2bSmlelstv #define IPMI_MSG_DATARCV 3 164dc766f2bSmlelstv 165dc766f2bSmlelstv #define IPMI_SENSOR_TYPE_TEMP 0x0101 166dc766f2bSmlelstv #define IPMI_SENSOR_TYPE_VOLT 0x0102 167dc766f2bSmlelstv #define IPMI_SENSOR_TYPE_FAN 0x0104 168dc766f2bSmlelstv #define IPMI_SENSOR_TYPE_INTRUSION 0x6F05 169dc766f2bSmlelstv #define IPMI_SENSOR_TYPE_PWRSUPPLY 0x6F08 170dc766f2bSmlelstv 171dc766f2bSmlelstv #define IPMI_NAME_UNICODE 0x00 172dc766f2bSmlelstv #define IPMI_NAME_BCDPLUS 0x01 173dc766f2bSmlelstv #define IPMI_NAME_ASCII6BIT 0x02 174dc766f2bSmlelstv #define IPMI_NAME_ASCII8BIT 0x03 175dc766f2bSmlelstv 176dc766f2bSmlelstv #define IPMI_ENTITY_PWRSUPPLY 0x0A 177dc766f2bSmlelstv 178dc766f2bSmlelstv #define IPMI_SENSOR_SCANNING_ENABLED (1L << 6) 179dc766f2bSmlelstv #define IPMI_SENSOR_UNAVAILABLE (1L << 5) 180dc766f2bSmlelstv #define IPMI_INVALID_SENSOR_P(x) \ 181dc766f2bSmlelstv (((x) & (IPMI_SENSOR_SCANNING_ENABLED|IPMI_SENSOR_UNAVAILABLE)) \ 182dc766f2bSmlelstv != IPMI_SENSOR_SCANNING_ENABLED) 183dc766f2bSmlelstv 184dc766f2bSmlelstv #define IPMI_SDR_TYPEFULL 1 185dc766f2bSmlelstv #define IPMI_SDR_TYPECOMPACT 2 186dc766f2bSmlelstv 187dc766f2bSmlelstv #define byteof(x) ((x) >> 3) 188dc766f2bSmlelstv #define bitof(x) (1L << ((x) & 0x7)) 189dc766f2bSmlelstv #define TB(b,m) (data[2+byteof(b)] & bitof(b)) 190dc766f2bSmlelstv 191dc766f2bSmlelstv #define dbg_printf(lvl, fmt...) \ 192dc766f2bSmlelstv if (ipmi_dbg >= lvl) \ 193dc766f2bSmlelstv printf(fmt); 194dc766f2bSmlelstv #define dbg_dump(lvl, msg, len, buf) \ 195dc766f2bSmlelstv if (len && ipmi_dbg >= lvl) \ 196dc766f2bSmlelstv dumpb(msg, len, (const uint8_t *)(buf)); 197dc766f2bSmlelstv 198dc766f2bSmlelstv static long signextend(unsigned long, int); 199dc766f2bSmlelstv 200dc766f2bSmlelstv SLIST_HEAD(ipmi_sensors_head, ipmi_sensor); 201dc766f2bSmlelstv static struct ipmi_sensors_head ipmi_sensor_list = 202dc766f2bSmlelstv SLIST_HEAD_INITIALIZER(&ipmi_sensor_list); 203dc766f2bSmlelstv 204dc766f2bSmlelstv static void dumpb(const char *, int, const uint8_t *); 205dc766f2bSmlelstv 206dc766f2bSmlelstv static int read_sensor(struct ipmi_softc *, struct ipmi_sensor *); 207dc766f2bSmlelstv static int add_sdr_sensor(struct ipmi_softc *, uint8_t *); 208dc766f2bSmlelstv static int get_sdr_partial(struct ipmi_softc *, uint16_t, uint16_t, 209dc766f2bSmlelstv uint8_t, uint8_t, void *, uint16_t *); 210dc766f2bSmlelstv static int get_sdr(struct ipmi_softc *, uint16_t, uint16_t *); 211dc766f2bSmlelstv 212dc766f2bSmlelstv static char *ipmi_buf_acquire(struct ipmi_softc *, size_t); 213dc766f2bSmlelstv static void ipmi_buf_release(struct ipmi_softc *, char *); 214dc766f2bSmlelstv static int ipmi_sendcmd(struct ipmi_softc *, int, int, int, int, int, const void*); 215dc766f2bSmlelstv static int ipmi_recvcmd(struct ipmi_softc *, int, int *, void *); 216dc766f2bSmlelstv static void ipmi_delay(struct ipmi_softc *, int); 217dc766f2bSmlelstv 218f95aa101Smlelstv static int ipmi_get_device_id(struct ipmi_softc *, struct ipmi_device_id *); 219dc766f2bSmlelstv static int ipmi_watchdog_setmode(struct sysmon_wdog *); 220dc766f2bSmlelstv static int ipmi_watchdog_tickle(struct sysmon_wdog *); 221dc766f2bSmlelstv static void ipmi_dotickle(struct ipmi_softc *); 222dc766f2bSmlelstv 223dc766f2bSmlelstv #if 0 224dc766f2bSmlelstv static int ipmi_intr(void *); 225dc766f2bSmlelstv #endif 226dc766f2bSmlelstv 227dc766f2bSmlelstv static int ipmi_match(device_t, cfdata_t, void *); 228dc766f2bSmlelstv static void ipmi_attach(device_t, device_t, void *); 229dc766f2bSmlelstv static int ipmi_detach(device_t, int); 230dc766f2bSmlelstv 231dc766f2bSmlelstv static long ipmi_convert(uint8_t, struct sdrtype1 *, long); 232dc766f2bSmlelstv static void ipmi_sensor_name(char *, int, uint8_t, uint8_t *); 233dc766f2bSmlelstv 234dc766f2bSmlelstv /* BMC Helper Functions */ 235dc766f2bSmlelstv static uint8_t bmc_read(struct ipmi_softc *, int); 236dc766f2bSmlelstv static void bmc_write(struct ipmi_softc *, int, uint8_t); 237dc766f2bSmlelstv static int bmc_io_wait(struct ipmi_softc *, int, uint8_t, uint8_t, const char *); 238dc766f2bSmlelstv static int bmc_io_wait_spin(struct ipmi_softc *, int, uint8_t, uint8_t); 239dc766f2bSmlelstv static int bmc_io_wait_sleep(struct ipmi_softc *, int, uint8_t, uint8_t); 240dc766f2bSmlelstv 241dc766f2bSmlelstv static void *cmn_buildmsg(struct ipmi_softc *, int, int, int, const void *, int *); 242dc766f2bSmlelstv 243dc766f2bSmlelstv static int getbits(uint8_t *, int, int); 244dc766f2bSmlelstv static int ipmi_sensor_type(int, int, int); 245dc766f2bSmlelstv 246dc766f2bSmlelstv static void ipmi_refresh_sensors(struct ipmi_softc *); 247dc766f2bSmlelstv static int ipmi_map_regs(struct ipmi_softc *, struct ipmi_attach_args *); 248dc766f2bSmlelstv static void ipmi_unmap_regs(struct ipmi_softc *); 249dc766f2bSmlelstv 250dc766f2bSmlelstv static int32_t ipmi_convert_sensor(uint8_t *, struct ipmi_sensor *); 251dc766f2bSmlelstv static void ipmi_set_limits(struct sysmon_envsys *, envsys_data_t *, 252dc766f2bSmlelstv sysmon_envsys_lim_t *, uint32_t *); 253dc766f2bSmlelstv static void ipmi_get_limits(struct sysmon_envsys *, envsys_data_t *, 254dc766f2bSmlelstv sysmon_envsys_lim_t *, uint32_t *); 255dc766f2bSmlelstv static void ipmi_get_sensor_limits(struct ipmi_softc *, struct ipmi_sensor *, 256dc766f2bSmlelstv sysmon_envsys_lim_t *, uint32_t *); 257dc766f2bSmlelstv static int ipmi_sensor_status(struct ipmi_softc *, struct ipmi_sensor *, 258dc766f2bSmlelstv envsys_data_t *, uint8_t *); 259dc766f2bSmlelstv 260dc766f2bSmlelstv static int add_child_sensors(struct ipmi_softc *, uint8_t *, int, int, int, 261dc766f2bSmlelstv int, int, int, const char *); 262dc766f2bSmlelstv 263dc766f2bSmlelstv static bool ipmi_suspend(device_t, const pmf_qual_t *); 264dc766f2bSmlelstv 265dc766f2bSmlelstv static int kcs_probe(struct ipmi_softc *); 266dc766f2bSmlelstv static int kcs_reset(struct ipmi_softc *); 267dc766f2bSmlelstv static int kcs_sendmsg(struct ipmi_softc *, int, const uint8_t *); 268dc766f2bSmlelstv static int kcs_recvmsg(struct ipmi_softc *, int, int *len, uint8_t *); 269dc766f2bSmlelstv 270f219451cSmlelstv static void *bt_buildmsg(struct ipmi_softc *, int, int, int, const void *, int *); 271dc766f2bSmlelstv static int bt_probe(struct ipmi_softc *); 272dc766f2bSmlelstv static int bt_reset(struct ipmi_softc *); 273dc766f2bSmlelstv static int bt_sendmsg(struct ipmi_softc *, int, const uint8_t *); 274dc766f2bSmlelstv static int bt_recvmsg(struct ipmi_softc *, int, int *, uint8_t *); 275dc766f2bSmlelstv 276dc766f2bSmlelstv static int smic_probe(struct ipmi_softc *); 277dc766f2bSmlelstv static int smic_reset(struct ipmi_softc *); 278dc766f2bSmlelstv static int smic_sendmsg(struct ipmi_softc *, int, const uint8_t *); 279dc766f2bSmlelstv static int smic_recvmsg(struct ipmi_softc *, int, int *, uint8_t *); 280dc766f2bSmlelstv 281dc766f2bSmlelstv static struct ipmi_if kcs_if = { 282dc766f2bSmlelstv "KCS", 283dc766f2bSmlelstv IPMI_IF_KCS_NREGS, 284dc766f2bSmlelstv cmn_buildmsg, 285dc766f2bSmlelstv kcs_sendmsg, 286dc766f2bSmlelstv kcs_recvmsg, 287dc766f2bSmlelstv kcs_reset, 288dc766f2bSmlelstv kcs_probe, 289dc766f2bSmlelstv }; 290dc766f2bSmlelstv 291dc766f2bSmlelstv static struct ipmi_if smic_if = { 292dc766f2bSmlelstv "SMIC", 293dc766f2bSmlelstv IPMI_IF_SMIC_NREGS, 294dc766f2bSmlelstv cmn_buildmsg, 295dc766f2bSmlelstv smic_sendmsg, 296dc766f2bSmlelstv smic_recvmsg, 297dc766f2bSmlelstv smic_reset, 298dc766f2bSmlelstv smic_probe, 299dc766f2bSmlelstv }; 300dc766f2bSmlelstv 301dc766f2bSmlelstv static struct ipmi_if bt_if = { 302dc766f2bSmlelstv "BT", 303dc766f2bSmlelstv IPMI_IF_BT_NREGS, 304dc766f2bSmlelstv bt_buildmsg, 305dc766f2bSmlelstv bt_sendmsg, 306dc766f2bSmlelstv bt_recvmsg, 307dc766f2bSmlelstv bt_reset, 308dc766f2bSmlelstv bt_probe, 309dc766f2bSmlelstv }; 310dc766f2bSmlelstv 311dc766f2bSmlelstv static struct ipmi_if *ipmi_get_if(int); 312dc766f2bSmlelstv 313dc766f2bSmlelstv static struct ipmi_if * 314dc766f2bSmlelstv ipmi_get_if(int iftype) 315dc766f2bSmlelstv { 316dc766f2bSmlelstv switch (iftype) { 317dc766f2bSmlelstv case IPMI_IF_KCS: 318dc766f2bSmlelstv return &kcs_if; 319dc766f2bSmlelstv case IPMI_IF_SMIC: 320dc766f2bSmlelstv return &smic_if; 321dc766f2bSmlelstv case IPMI_IF_BT: 322dc766f2bSmlelstv return &bt_if; 323dc766f2bSmlelstv default: 324dc766f2bSmlelstv return NULL; 325dc766f2bSmlelstv } 326dc766f2bSmlelstv } 327dc766f2bSmlelstv 328dc766f2bSmlelstv /* 329dc766f2bSmlelstv * BMC Helper Functions 330dc766f2bSmlelstv */ 331dc766f2bSmlelstv static uint8_t 332dc766f2bSmlelstv bmc_read(struct ipmi_softc *sc, int offset) 333dc766f2bSmlelstv { 334dc766f2bSmlelstv return bus_space_read_1(sc->sc_iot, sc->sc_ioh, 335dc766f2bSmlelstv offset * sc->sc_if_iospacing); 336dc766f2bSmlelstv } 337dc766f2bSmlelstv 338dc766f2bSmlelstv static void 339dc766f2bSmlelstv bmc_write(struct ipmi_softc *sc, int offset, uint8_t val) 340dc766f2bSmlelstv { 341dc766f2bSmlelstv bus_space_write_1(sc->sc_iot, sc->sc_ioh, 342dc766f2bSmlelstv offset * sc->sc_if_iospacing, val); 343dc766f2bSmlelstv } 344dc766f2bSmlelstv 345dc766f2bSmlelstv static int 346dc766f2bSmlelstv bmc_io_wait_sleep(struct ipmi_softc *sc, int offset, uint8_t mask, 347dc766f2bSmlelstv uint8_t value) 348dc766f2bSmlelstv { 349dc766f2bSmlelstv int retries; 350dc766f2bSmlelstv uint8_t v; 351dc766f2bSmlelstv 352dc766f2bSmlelstv KASSERT(mutex_owned(&sc->sc_cmd_mtx)); 353dc766f2bSmlelstv 354dc766f2bSmlelstv for (retries = 0; retries < sc->sc_max_retries; retries++) { 355dc766f2bSmlelstv v = bmc_read(sc, offset); 356dc766f2bSmlelstv if ((v & mask) == value) 357dc766f2bSmlelstv return v; 3582ec61c4fSriastradh kpause("ipmicmd", /*intr*/false, /*timo*/1, /*mtx*/NULL); 359dc766f2bSmlelstv } 360dc766f2bSmlelstv return -1; 361dc766f2bSmlelstv } 362dc766f2bSmlelstv 363dc766f2bSmlelstv static int 364dc766f2bSmlelstv bmc_io_wait(struct ipmi_softc *sc, int offset, uint8_t mask, uint8_t value, 365dc766f2bSmlelstv const char *lbl) 366dc766f2bSmlelstv { 367dc766f2bSmlelstv int v; 368dc766f2bSmlelstv 369dc766f2bSmlelstv v = bmc_io_wait_spin(sc, offset, mask, value); 370dc766f2bSmlelstv if (cold || v != -1) 371dc766f2bSmlelstv return v; 372dc766f2bSmlelstv 373dc766f2bSmlelstv return bmc_io_wait_sleep(sc, offset, mask, value); 374dc766f2bSmlelstv } 375dc766f2bSmlelstv 376dc766f2bSmlelstv static int 377dc766f2bSmlelstv bmc_io_wait_spin(struct ipmi_softc *sc, int offset, uint8_t mask, 378dc766f2bSmlelstv uint8_t value) 379dc766f2bSmlelstv { 380dc766f2bSmlelstv uint8_t v; 381dc766f2bSmlelstv int count = cold ? 15000 : 500; 382dc766f2bSmlelstv /* ~us */ 383dc766f2bSmlelstv 384dc766f2bSmlelstv while (count--) { 385dc766f2bSmlelstv v = bmc_read(sc, offset); 386dc766f2bSmlelstv if ((v & mask) == value) 387dc766f2bSmlelstv return v; 388dc766f2bSmlelstv 389dc766f2bSmlelstv delay(1); 390dc766f2bSmlelstv } 391dc766f2bSmlelstv 392dc766f2bSmlelstv return -1; 393dc766f2bSmlelstv 394dc766f2bSmlelstv } 395dc766f2bSmlelstv 396dc766f2bSmlelstv #define NETFN_LUN(nf,ln) (((nf) << 2) | ((ln) & 0x3)) 397b1f23fc9Smlelstv #define GET_NETFN(m) (((m) >> 2) 398b1f23fc9Smlelstv #define GET_LUN(m) ((m) & 0x03) 399dc766f2bSmlelstv 400dc766f2bSmlelstv /* 401dc766f2bSmlelstv * BT interface 402dc766f2bSmlelstv */ 403dc766f2bSmlelstv #define _BT_CTRL_REG 0 404dc766f2bSmlelstv #define BT_CLR_WR_PTR (1L << 0) 405dc766f2bSmlelstv #define BT_CLR_RD_PTR (1L << 1) 406dc766f2bSmlelstv #define BT_HOST2BMC_ATN (1L << 2) 407dc766f2bSmlelstv #define BT_BMC2HOST_ATN (1L << 3) 408dc766f2bSmlelstv #define BT_EVT_ATN (1L << 4) 409dc766f2bSmlelstv #define BT_HOST_BUSY (1L << 6) 410dc766f2bSmlelstv #define BT_BMC_BUSY (1L << 7) 411dc766f2bSmlelstv 412dc766f2bSmlelstv #define BT_READY (BT_HOST_BUSY|BT_HOST2BMC_ATN|BT_BMC2HOST_ATN) 413dc766f2bSmlelstv 414dc766f2bSmlelstv #define _BT_DATAIN_REG 1 415dc766f2bSmlelstv #define _BT_DATAOUT_REG 1 416dc766f2bSmlelstv 417dc766f2bSmlelstv #define _BT_INTMASK_REG 2 418dc766f2bSmlelstv #define BT_IM_HIRQ_PEND (1L << 1) 419dc766f2bSmlelstv #define BT_IM_SCI_EN (1L << 2) 420dc766f2bSmlelstv #define BT_IM_SMI_EN (1L << 3) 421dc766f2bSmlelstv #define BT_IM_NMI2SMI (1L << 4) 422dc766f2bSmlelstv 423dc766f2bSmlelstv static int bt_read(struct ipmi_softc *, int); 424dc766f2bSmlelstv static int bt_write(struct ipmi_softc *, int, uint8_t); 425dc766f2bSmlelstv 426dc766f2bSmlelstv static int 427dc766f2bSmlelstv bt_read(struct ipmi_softc *sc, int reg) 428dc766f2bSmlelstv { 429dc766f2bSmlelstv return bmc_read(sc, reg); 430dc766f2bSmlelstv } 431dc766f2bSmlelstv 432dc766f2bSmlelstv static int 433dc766f2bSmlelstv bt_write(struct ipmi_softc *sc, int reg, uint8_t data) 434dc766f2bSmlelstv { 435dc766f2bSmlelstv if (bmc_io_wait(sc, _BT_CTRL_REG, BT_BMC_BUSY, 0, __func__) < 0) 436dc766f2bSmlelstv return -1; 437dc766f2bSmlelstv 438dc766f2bSmlelstv bmc_write(sc, reg, data); 439dc766f2bSmlelstv return 0; 440dc766f2bSmlelstv } 441dc766f2bSmlelstv 442dc766f2bSmlelstv static int 443dc766f2bSmlelstv bt_sendmsg(struct ipmi_softc *sc, int len, const uint8_t *data) 444dc766f2bSmlelstv { 445dc766f2bSmlelstv int i; 446dc766f2bSmlelstv 447dc766f2bSmlelstv bt_write(sc, _BT_CTRL_REG, BT_CLR_WR_PTR); 448dc766f2bSmlelstv for (i = 0; i < len; i++) 449dc766f2bSmlelstv bt_write(sc, _BT_DATAOUT_REG, data[i]); 450dc766f2bSmlelstv 451dc766f2bSmlelstv bt_write(sc, _BT_CTRL_REG, BT_HOST2BMC_ATN); 452dc766f2bSmlelstv if (bmc_io_wait(sc, _BT_CTRL_REG, BT_HOST2BMC_ATN | BT_BMC_BUSY, 0, 453dc766f2bSmlelstv __func__) < 0) 454dc766f2bSmlelstv return -1; 455dc766f2bSmlelstv 456dc766f2bSmlelstv return 0; 457dc766f2bSmlelstv } 458dc766f2bSmlelstv 459dc766f2bSmlelstv static int 460dc766f2bSmlelstv bt_recvmsg(struct ipmi_softc *sc, int maxlen, int *rxlen, uint8_t *data) 461dc766f2bSmlelstv { 462dc766f2bSmlelstv uint8_t len, v, i; 463dc766f2bSmlelstv 464dc766f2bSmlelstv if (bmc_io_wait(sc, _BT_CTRL_REG, BT_BMC2HOST_ATN, BT_BMC2HOST_ATN, 465dc766f2bSmlelstv __func__) < 0) 466dc766f2bSmlelstv return -1; 467dc766f2bSmlelstv 468dc766f2bSmlelstv bt_write(sc, _BT_CTRL_REG, BT_HOST_BUSY); 469dc766f2bSmlelstv bt_write(sc, _BT_CTRL_REG, BT_BMC2HOST_ATN); 470dc766f2bSmlelstv bt_write(sc, _BT_CTRL_REG, BT_CLR_RD_PTR); 471dc766f2bSmlelstv len = bt_read(sc, _BT_DATAIN_REG); 472dc766f2bSmlelstv for (i = IPMI_BTMSG_NFLN; i <= len; i++) { 473dc766f2bSmlelstv v = bt_read(sc, _BT_DATAIN_REG); 474dc766f2bSmlelstv if (i != IPMI_BTMSG_SEQ) 475dc766f2bSmlelstv *(data++) = v; 476dc766f2bSmlelstv } 477dc766f2bSmlelstv bt_write(sc, _BT_CTRL_REG, BT_HOST_BUSY); 478dc766f2bSmlelstv *rxlen = len - 1; 479dc766f2bSmlelstv 480dc766f2bSmlelstv return 0; 481dc766f2bSmlelstv } 482dc766f2bSmlelstv 483dc766f2bSmlelstv static int 484dc766f2bSmlelstv bt_reset(struct ipmi_softc *sc) 485dc766f2bSmlelstv { 486dc766f2bSmlelstv return -1; 487dc766f2bSmlelstv } 488dc766f2bSmlelstv 489dc766f2bSmlelstv static int 490dc766f2bSmlelstv bt_probe(struct ipmi_softc *sc) 491dc766f2bSmlelstv { 492dc766f2bSmlelstv uint8_t rv; 493dc766f2bSmlelstv 494dc766f2bSmlelstv rv = bmc_read(sc, _BT_CTRL_REG); 495dc766f2bSmlelstv rv &= BT_HOST_BUSY; 496dc766f2bSmlelstv rv |= BT_CLR_WR_PTR|BT_CLR_RD_PTR|BT_BMC2HOST_ATN|BT_HOST2BMC_ATN; 497dc766f2bSmlelstv bmc_write(sc, _BT_CTRL_REG, rv); 498dc766f2bSmlelstv 499dc766f2bSmlelstv rv = bmc_read(sc, _BT_INTMASK_REG); 500dc766f2bSmlelstv rv &= BT_IM_SCI_EN|BT_IM_SMI_EN|BT_IM_NMI2SMI; 501dc766f2bSmlelstv rv |= BT_IM_HIRQ_PEND; 502dc766f2bSmlelstv bmc_write(sc, _BT_INTMASK_REG, rv); 503dc766f2bSmlelstv 504dc766f2bSmlelstv #if 0 505dc766f2bSmlelstv printf("%s: %2x\n", __func__, v); 506dc766f2bSmlelstv printf(" WR : %2x\n", v & BT_CLR_WR_PTR); 507dc766f2bSmlelstv printf(" RD : %2x\n", v & BT_CLR_RD_PTR); 508dc766f2bSmlelstv printf(" H2B : %2x\n", v & BT_HOST2BMC_ATN); 509dc766f2bSmlelstv printf(" B2H : %2x\n", v & BT_BMC2HOST_ATN); 510dc766f2bSmlelstv printf(" EVT : %2x\n", v & BT_EVT_ATN); 511dc766f2bSmlelstv printf(" HBSY : %2x\n", v & BT_HOST_BUSY); 512dc766f2bSmlelstv printf(" BBSY : %2x\n", v & BT_BMC_BUSY); 513dc766f2bSmlelstv #endif 514dc766f2bSmlelstv return 0; 515dc766f2bSmlelstv } 516dc766f2bSmlelstv 517dc766f2bSmlelstv /* 518dc766f2bSmlelstv * SMIC interface 519dc766f2bSmlelstv */ 520dc766f2bSmlelstv #define _SMIC_DATAIN_REG 0 521dc766f2bSmlelstv #define _SMIC_DATAOUT_REG 0 522dc766f2bSmlelstv 523dc766f2bSmlelstv #define _SMIC_CTRL_REG 1 524dc766f2bSmlelstv #define SMS_CC_GET_STATUS 0x40 525dc766f2bSmlelstv #define SMS_CC_START_TRANSFER 0x41 526dc766f2bSmlelstv #define SMS_CC_NEXT_TRANSFER 0x42 527dc766f2bSmlelstv #define SMS_CC_END_TRANSFER 0x43 528dc766f2bSmlelstv #define SMS_CC_START_RECEIVE 0x44 529dc766f2bSmlelstv #define SMS_CC_NEXT_RECEIVE 0x45 530dc766f2bSmlelstv #define SMS_CC_END_RECEIVE 0x46 531dc766f2bSmlelstv #define SMS_CC_TRANSFER_ABORT 0x47 532dc766f2bSmlelstv 533dc766f2bSmlelstv #define SMS_SC_READY 0xc0 534dc766f2bSmlelstv #define SMS_SC_WRITE_START 0xc1 535dc766f2bSmlelstv #define SMS_SC_WRITE_NEXT 0xc2 536dc766f2bSmlelstv #define SMS_SC_WRITE_END 0xc3 537dc766f2bSmlelstv #define SMS_SC_READ_START 0xc4 538dc766f2bSmlelstv #define SMS_SC_READ_NEXT 0xc5 539dc766f2bSmlelstv #define SMS_SC_READ_END 0xc6 540dc766f2bSmlelstv 541dc766f2bSmlelstv #define _SMIC_FLAG_REG 2 542dc766f2bSmlelstv #define SMIC_BUSY (1L << 0) 543dc766f2bSmlelstv #define SMIC_SMS_ATN (1L << 2) 544dc766f2bSmlelstv #define SMIC_EVT_ATN (1L << 3) 545dc766f2bSmlelstv #define SMIC_SMI (1L << 4) 546dc766f2bSmlelstv #define SMIC_TX_DATA_RDY (1L << 6) 547dc766f2bSmlelstv #define SMIC_RX_DATA_RDY (1L << 7) 548dc766f2bSmlelstv 549dc766f2bSmlelstv static int smic_wait(struct ipmi_softc *, uint8_t, uint8_t, const char *); 550dc766f2bSmlelstv static int smic_write_cmd_data(struct ipmi_softc *, uint8_t, const uint8_t *); 551dc766f2bSmlelstv static int smic_read_data(struct ipmi_softc *, uint8_t *); 552dc766f2bSmlelstv 553dc766f2bSmlelstv static int 554dc766f2bSmlelstv smic_wait(struct ipmi_softc *sc, uint8_t mask, uint8_t val, const char *lbl) 555dc766f2bSmlelstv { 556dc766f2bSmlelstv int v; 557dc766f2bSmlelstv 558dc766f2bSmlelstv /* Wait for expected flag bits */ 559dc766f2bSmlelstv v = bmc_io_wait(sc, _SMIC_FLAG_REG, mask, val, __func__); 560dc766f2bSmlelstv if (v < 0) 561dc766f2bSmlelstv return -1; 562dc766f2bSmlelstv 563dc766f2bSmlelstv /* Return current status */ 564dc766f2bSmlelstv v = bmc_read(sc, _SMIC_CTRL_REG); 565dc766f2bSmlelstv dbg_printf(99, "%s(%s) = %#.2x\n", __func__, lbl, v); 566dc766f2bSmlelstv return v; 567dc766f2bSmlelstv } 568dc766f2bSmlelstv 569dc766f2bSmlelstv static int 570dc766f2bSmlelstv smic_write_cmd_data(struct ipmi_softc *sc, uint8_t cmd, const uint8_t *data) 571dc766f2bSmlelstv { 572dc766f2bSmlelstv int sts, v; 573dc766f2bSmlelstv 574dc766f2bSmlelstv dbg_printf(50, "%s: %#.2x %#.2x\n", __func__, cmd, data ? *data : -1); 575dc766f2bSmlelstv sts = smic_wait(sc, SMIC_TX_DATA_RDY | SMIC_BUSY, SMIC_TX_DATA_RDY, 576dc766f2bSmlelstv "smic_write_cmd_data ready"); 577dc766f2bSmlelstv if (sts < 0) 578dc766f2bSmlelstv return sts; 579dc766f2bSmlelstv 580dc766f2bSmlelstv bmc_write(sc, _SMIC_CTRL_REG, cmd); 581dc766f2bSmlelstv if (data) 582dc766f2bSmlelstv bmc_write(sc, _SMIC_DATAOUT_REG, *data); 583dc766f2bSmlelstv 584dc766f2bSmlelstv /* Toggle BUSY bit, then wait for busy bit to clear */ 585dc766f2bSmlelstv v = bmc_read(sc, _SMIC_FLAG_REG); 586dc766f2bSmlelstv bmc_write(sc, _SMIC_FLAG_REG, v | SMIC_BUSY); 587dc766f2bSmlelstv 588dc766f2bSmlelstv return smic_wait(sc, SMIC_BUSY, 0, __func__); 589dc766f2bSmlelstv } 590dc766f2bSmlelstv 591dc766f2bSmlelstv static int 592dc766f2bSmlelstv smic_read_data(struct ipmi_softc *sc, uint8_t *data) 593dc766f2bSmlelstv { 594dc766f2bSmlelstv int sts; 595dc766f2bSmlelstv 596dc766f2bSmlelstv sts = smic_wait(sc, SMIC_RX_DATA_RDY | SMIC_BUSY, SMIC_RX_DATA_RDY, 597dc766f2bSmlelstv __func__); 598dc766f2bSmlelstv if (sts >= 0) { 599dc766f2bSmlelstv *data = bmc_read(sc, _SMIC_DATAIN_REG); 600dc766f2bSmlelstv dbg_printf(50, "%s: %#.2x\n", __func__, *data); 601dc766f2bSmlelstv } 602dc766f2bSmlelstv return sts; 603dc766f2bSmlelstv } 604dc766f2bSmlelstv 605dc766f2bSmlelstv #define ErrStat(a, ...) if (a) printf(__VA_ARGS__); 606dc766f2bSmlelstv 607dc766f2bSmlelstv static int 608dc766f2bSmlelstv smic_sendmsg(struct ipmi_softc *sc, int len, const uint8_t *data) 609dc766f2bSmlelstv { 610dc766f2bSmlelstv int sts, idx; 611dc766f2bSmlelstv 612dc766f2bSmlelstv sts = smic_write_cmd_data(sc, SMS_CC_START_TRANSFER, &data[0]); 613dc766f2bSmlelstv ErrStat(sts != SMS_SC_WRITE_START, "%s: wstart", __func__); 614dc766f2bSmlelstv for (idx = 1; idx < len - 1; idx++) { 615dc766f2bSmlelstv sts = smic_write_cmd_data(sc, SMS_CC_NEXT_TRANSFER, 616dc766f2bSmlelstv &data[idx]); 617dc766f2bSmlelstv ErrStat(sts != SMS_SC_WRITE_NEXT, "%s: write", __func__); 618dc766f2bSmlelstv } 619dc766f2bSmlelstv sts = smic_write_cmd_data(sc, SMS_CC_END_TRANSFER, &data[idx]); 620dc766f2bSmlelstv if (sts != SMS_SC_WRITE_END) { 621dc766f2bSmlelstv dbg_printf(50, "%s: %d/%d = %#.2x\n", __func__, idx, len, sts); 622dc766f2bSmlelstv return -1; 623dc766f2bSmlelstv } 624dc766f2bSmlelstv 625dc766f2bSmlelstv return 0; 626dc766f2bSmlelstv } 627dc766f2bSmlelstv 628dc766f2bSmlelstv static int 629dc766f2bSmlelstv smic_recvmsg(struct ipmi_softc *sc, int maxlen, int *len, uint8_t *data) 630dc766f2bSmlelstv { 631dc766f2bSmlelstv int sts, idx; 632dc766f2bSmlelstv 633dc766f2bSmlelstv *len = 0; 634dc766f2bSmlelstv sts = smic_wait(sc, SMIC_RX_DATA_RDY, SMIC_RX_DATA_RDY, __func__); 635dc766f2bSmlelstv if (sts < 0) 636dc766f2bSmlelstv return -1; 637dc766f2bSmlelstv 638dc766f2bSmlelstv sts = smic_write_cmd_data(sc, SMS_CC_START_RECEIVE, NULL); 639dc766f2bSmlelstv ErrStat(sts != SMS_SC_READ_START, "%s: rstart", __func__); 640dc766f2bSmlelstv for (idx = 0;; ) { 641dc766f2bSmlelstv sts = smic_read_data(sc, &data[idx++]); 642dc766f2bSmlelstv if (sts != SMS_SC_READ_START && sts != SMS_SC_READ_NEXT) 643dc766f2bSmlelstv break; 644dc766f2bSmlelstv smic_write_cmd_data(sc, SMS_CC_NEXT_RECEIVE, NULL); 645dc766f2bSmlelstv } 646dc766f2bSmlelstv ErrStat(sts != SMS_SC_READ_END, "%s: rend", __func__); 647dc766f2bSmlelstv 648dc766f2bSmlelstv *len = idx; 649dc766f2bSmlelstv 650dc766f2bSmlelstv sts = smic_write_cmd_data(sc, SMS_CC_END_RECEIVE, NULL); 651dc766f2bSmlelstv if (sts != SMS_SC_READY) { 652dc766f2bSmlelstv dbg_printf(50, "%s: %d/%d = %#.2x\n", 653dc766f2bSmlelstv __func__, idx, maxlen, sts); 654dc766f2bSmlelstv return -1; 655dc766f2bSmlelstv } 656dc766f2bSmlelstv 657dc766f2bSmlelstv return 0; 658dc766f2bSmlelstv } 659dc766f2bSmlelstv 660dc766f2bSmlelstv static int 661dc766f2bSmlelstv smic_reset(struct ipmi_softc *sc) 662dc766f2bSmlelstv { 663dc766f2bSmlelstv return -1; 664dc766f2bSmlelstv } 665dc766f2bSmlelstv 666dc766f2bSmlelstv static int 667dc766f2bSmlelstv smic_probe(struct ipmi_softc *sc) 668dc766f2bSmlelstv { 669dc766f2bSmlelstv /* Flag register should not be 0xFF on a good system */ 670dc766f2bSmlelstv if (bmc_read(sc, _SMIC_FLAG_REG) == 0xFF) 671dc766f2bSmlelstv return -1; 672dc766f2bSmlelstv 673dc766f2bSmlelstv return 0; 674dc766f2bSmlelstv } 675dc766f2bSmlelstv 676dc766f2bSmlelstv /* 677dc766f2bSmlelstv * KCS interface 678dc766f2bSmlelstv */ 679dc766f2bSmlelstv #define _KCS_DATAIN_REGISTER 0 680dc766f2bSmlelstv #define _KCS_DATAOUT_REGISTER 0 681dc766f2bSmlelstv #define KCS_READ_NEXT 0x68 682dc766f2bSmlelstv 683dc766f2bSmlelstv #define _KCS_COMMAND_REGISTER 1 684dc766f2bSmlelstv #define KCS_GET_STATUS 0x60 685dc766f2bSmlelstv #define KCS_WRITE_START 0x61 686dc766f2bSmlelstv #define KCS_WRITE_END 0x62 687dc766f2bSmlelstv 688dc766f2bSmlelstv #define _KCS_STATUS_REGISTER 1 689dc766f2bSmlelstv #define KCS_OBF (1L << 0) 690dc766f2bSmlelstv #define KCS_IBF (1L << 1) 691dc766f2bSmlelstv #define KCS_SMS_ATN (1L << 2) 692dc766f2bSmlelstv #define KCS_CD (1L << 3) 693dc766f2bSmlelstv #define KCS_OEM1 (1L << 4) 694dc766f2bSmlelstv #define KCS_OEM2 (1L << 5) 695dc766f2bSmlelstv #define KCS_STATE_MASK 0xc0 696dc766f2bSmlelstv #define KCS_IDLE_STATE 0x00 697dc766f2bSmlelstv #define KCS_READ_STATE 0x40 698dc766f2bSmlelstv #define KCS_WRITE_STATE 0x80 699dc766f2bSmlelstv #define KCS_ERROR_STATE 0xC0 700dc766f2bSmlelstv 701dc766f2bSmlelstv static int kcs_wait(struct ipmi_softc *, uint8_t, uint8_t, const char *); 702dc766f2bSmlelstv static int kcs_write_cmd(struct ipmi_softc *, uint8_t); 703dc766f2bSmlelstv static int kcs_write_data(struct ipmi_softc *, uint8_t); 704dc766f2bSmlelstv static int kcs_read_data(struct ipmi_softc *, uint8_t *); 705dc766f2bSmlelstv 706dc766f2bSmlelstv static int 707dc766f2bSmlelstv kcs_wait(struct ipmi_softc *sc, uint8_t mask, uint8_t value, const char *lbl) 708dc766f2bSmlelstv { 709dc766f2bSmlelstv int v; 710dc766f2bSmlelstv 711dc766f2bSmlelstv v = bmc_io_wait(sc, _KCS_STATUS_REGISTER, mask, value, lbl); 712dc766f2bSmlelstv if (v < 0) 713dc766f2bSmlelstv return v; 714dc766f2bSmlelstv 715dc766f2bSmlelstv /* Check if output buffer full, read dummy byte */ 716dc766f2bSmlelstv if ((v & (KCS_OBF | KCS_STATE_MASK)) == (KCS_OBF | KCS_WRITE_STATE)) 717dc766f2bSmlelstv bmc_read(sc, _KCS_DATAIN_REGISTER); 718dc766f2bSmlelstv 719dc766f2bSmlelstv /* Check for error state */ 720dc766f2bSmlelstv if ((v & KCS_STATE_MASK) == KCS_ERROR_STATE) { 721dc766f2bSmlelstv bmc_write(sc, _KCS_COMMAND_REGISTER, KCS_GET_STATUS); 722dc766f2bSmlelstv while (bmc_read(sc, _KCS_STATUS_REGISTER) & KCS_IBF) 723dc766f2bSmlelstv ; 724dc766f2bSmlelstv aprint_error_dev(sc->sc_dev, "error code: %#x\n", 725dc766f2bSmlelstv bmc_read(sc, _KCS_DATAIN_REGISTER)); 726dc766f2bSmlelstv } 727dc766f2bSmlelstv 728dc766f2bSmlelstv return v & KCS_STATE_MASK; 729dc766f2bSmlelstv } 730dc766f2bSmlelstv 731dc766f2bSmlelstv static int 732dc766f2bSmlelstv kcs_write_cmd(struct ipmi_softc *sc, uint8_t cmd) 733dc766f2bSmlelstv { 734dc766f2bSmlelstv /* ASSERT: IBF and OBF are clear */ 735dc766f2bSmlelstv dbg_printf(50, "%s: %#.2x\n", __func__, cmd); 736dc766f2bSmlelstv bmc_write(sc, _KCS_COMMAND_REGISTER, cmd); 737dc766f2bSmlelstv 738dc766f2bSmlelstv return kcs_wait(sc, KCS_IBF, 0, "write_cmd"); 739dc766f2bSmlelstv } 740dc766f2bSmlelstv 741dc766f2bSmlelstv static int 742dc766f2bSmlelstv kcs_write_data(struct ipmi_softc *sc, uint8_t data) 743dc766f2bSmlelstv { 744dc766f2bSmlelstv /* ASSERT: IBF and OBF are clear */ 745dc766f2bSmlelstv dbg_printf(50, "%s: %#.2x\n", __func__, data); 746dc766f2bSmlelstv bmc_write(sc, _KCS_DATAOUT_REGISTER, data); 747dc766f2bSmlelstv 748dc766f2bSmlelstv return kcs_wait(sc, KCS_IBF, 0, "write_data"); 749dc766f2bSmlelstv } 750dc766f2bSmlelstv 751dc766f2bSmlelstv static int 752dc766f2bSmlelstv kcs_read_data(struct ipmi_softc *sc, uint8_t * data) 753dc766f2bSmlelstv { 754dc766f2bSmlelstv int sts; 755dc766f2bSmlelstv 756dc766f2bSmlelstv sts = kcs_wait(sc, KCS_IBF | KCS_OBF, KCS_OBF, __func__); 757dc766f2bSmlelstv if (sts != KCS_READ_STATE) 758dc766f2bSmlelstv return sts; 759dc766f2bSmlelstv 760dc766f2bSmlelstv /* ASSERT: OBF is set read data, request next byte */ 761dc766f2bSmlelstv *data = bmc_read(sc, _KCS_DATAIN_REGISTER); 762dc766f2bSmlelstv bmc_write(sc, _KCS_DATAOUT_REGISTER, KCS_READ_NEXT); 763dc766f2bSmlelstv 764dc766f2bSmlelstv dbg_printf(50, "%s: %#.2x\n", __func__, *data); 765dc766f2bSmlelstv 766dc766f2bSmlelstv return sts; 767dc766f2bSmlelstv } 768dc766f2bSmlelstv 769dc766f2bSmlelstv /* Exported KCS functions */ 770dc766f2bSmlelstv static int 771dc766f2bSmlelstv kcs_sendmsg(struct ipmi_softc *sc, int len, const uint8_t * data) 772dc766f2bSmlelstv { 773dc766f2bSmlelstv int idx, sts; 774dc766f2bSmlelstv 775dc766f2bSmlelstv /* ASSERT: IBF is clear */ 776dc766f2bSmlelstv dbg_dump(50, __func__, len, data); 777dc766f2bSmlelstv sts = kcs_write_cmd(sc, KCS_WRITE_START); 778dc766f2bSmlelstv for (idx = 0; idx < len; idx++) { 779dc766f2bSmlelstv if (idx == len - 1) 780dc766f2bSmlelstv sts = kcs_write_cmd(sc, KCS_WRITE_END); 781dc766f2bSmlelstv 782dc766f2bSmlelstv if (sts != KCS_WRITE_STATE) 783dc766f2bSmlelstv break; 784dc766f2bSmlelstv 785dc766f2bSmlelstv sts = kcs_write_data(sc, data[idx]); 786dc766f2bSmlelstv } 787dc766f2bSmlelstv if (sts != KCS_READ_STATE) { 788dc766f2bSmlelstv dbg_printf(1, "%s: %d/%d <%#.2x>\n", __func__, idx, len, sts); 789dc766f2bSmlelstv dbg_dump(1, __func__, len, data); 790dc766f2bSmlelstv return -1; 791dc766f2bSmlelstv } 792dc766f2bSmlelstv 793dc766f2bSmlelstv return 0; 794dc766f2bSmlelstv } 795dc766f2bSmlelstv 796dc766f2bSmlelstv static int 797dc766f2bSmlelstv kcs_recvmsg(struct ipmi_softc *sc, int maxlen, int *rxlen, uint8_t * data) 798dc766f2bSmlelstv { 799dc766f2bSmlelstv int idx, sts; 800dc766f2bSmlelstv 801dc766f2bSmlelstv for (idx = 0; idx < maxlen; idx++) { 802dc766f2bSmlelstv sts = kcs_read_data(sc, &data[idx]); 803dc766f2bSmlelstv if (sts != KCS_READ_STATE) 804dc766f2bSmlelstv break; 805dc766f2bSmlelstv } 806dc766f2bSmlelstv sts = kcs_wait(sc, KCS_IBF, 0, __func__); 807dc766f2bSmlelstv *rxlen = idx; 808dc766f2bSmlelstv if (sts != KCS_IDLE_STATE) { 809dc766f2bSmlelstv dbg_printf(1, "%s: %d/%d <%#.2x>\n", 810dc766f2bSmlelstv __func__, idx, maxlen, sts); 811dc766f2bSmlelstv return -1; 812dc766f2bSmlelstv } 813dc766f2bSmlelstv 814dc766f2bSmlelstv dbg_dump(50, __func__, idx, data); 815dc766f2bSmlelstv 816dc766f2bSmlelstv return 0; 817dc766f2bSmlelstv } 818dc766f2bSmlelstv 819dc766f2bSmlelstv static int 820dc766f2bSmlelstv kcs_reset(struct ipmi_softc *sc) 821dc766f2bSmlelstv { 822dc766f2bSmlelstv return -1; 823dc766f2bSmlelstv } 824dc766f2bSmlelstv 825dc766f2bSmlelstv static int 826dc766f2bSmlelstv kcs_probe(struct ipmi_softc *sc) 827dc766f2bSmlelstv { 828dc766f2bSmlelstv uint8_t v; 829dc766f2bSmlelstv 830dc766f2bSmlelstv v = bmc_read(sc, _KCS_STATUS_REGISTER); 831dc766f2bSmlelstv #if 0 832dc766f2bSmlelstv printf("%s: %2x\n", __func__, v); 833dc766f2bSmlelstv printf(" STS: %2x\n", v & KCS_STATE_MASK); 834dc766f2bSmlelstv printf(" ATN: %2x\n", v & KCS_SMS_ATN); 835dc766f2bSmlelstv printf(" C/D: %2x\n", v & KCS_CD); 836dc766f2bSmlelstv printf(" IBF: %2x\n", v & KCS_IBF); 837dc766f2bSmlelstv printf(" OBF: %2x\n", v & KCS_OBF); 838dc766f2bSmlelstv #else 839dc766f2bSmlelstv __USE(v); 840dc766f2bSmlelstv #endif 841dc766f2bSmlelstv return 0; 842dc766f2bSmlelstv } 843dc766f2bSmlelstv 844dc766f2bSmlelstv /* 845dc766f2bSmlelstv * IPMI code 846dc766f2bSmlelstv */ 847dc766f2bSmlelstv #define READ_SMS_BUFFER 0x37 848dc766f2bSmlelstv #define WRITE_I2C 0x50 849dc766f2bSmlelstv 850dc766f2bSmlelstv #define GET_MESSAGE_CMD 0x33 851dc766f2bSmlelstv #define SEND_MESSAGE_CMD 0x34 852dc766f2bSmlelstv 853dc766f2bSmlelstv #define IPMB_CHANNEL_NUMBER 0 854dc766f2bSmlelstv 855dc766f2bSmlelstv #define PUBLIC_BUS 0 856dc766f2bSmlelstv 857dc766f2bSmlelstv #define MIN_I2C_PACKET_SIZE 3 858dc766f2bSmlelstv #define MIN_IMB_PACKET_SIZE 7 /* one byte for cksum */ 859dc766f2bSmlelstv 860dc766f2bSmlelstv #define MIN_BTBMC_REQ_SIZE 4 861dc766f2bSmlelstv #define MIN_BTBMC_RSP_SIZE 5 862dc766f2bSmlelstv #define MIN_BMC_REQ_SIZE 2 863dc766f2bSmlelstv #define MIN_BMC_RSP_SIZE 3 864dc766f2bSmlelstv 865dc766f2bSmlelstv #define BMC_SA 0x20 /* BMC/ESM3 */ 866dc766f2bSmlelstv #define FPC_SA 0x22 /* front panel */ 867dc766f2bSmlelstv #define BP_SA 0xC0 /* Primary Backplane */ 868dc766f2bSmlelstv #define BP2_SA 0xC2 /* Secondary Backplane */ 869dc766f2bSmlelstv #define PBP_SA 0xC4 /* Peripheral Backplane */ 870dc766f2bSmlelstv #define DRAC_SA 0x28 /* DRAC-III */ 871dc766f2bSmlelstv #define DRAC3_SA 0x30 /* DRAC-III */ 872dc766f2bSmlelstv #define BMC_LUN 0 873dc766f2bSmlelstv #define SMS_LUN 2 874dc766f2bSmlelstv 875dc766f2bSmlelstv struct ipmi_request { 876dc766f2bSmlelstv uint8_t rsSa; 877dc766f2bSmlelstv uint8_t rsLun; 878dc766f2bSmlelstv uint8_t netFn; 879dc766f2bSmlelstv uint8_t cmd; 880dc766f2bSmlelstv uint8_t data_len; 881dc766f2bSmlelstv uint8_t *data; 882dc766f2bSmlelstv }; 883dc766f2bSmlelstv 884dc766f2bSmlelstv struct ipmi_response { 885dc766f2bSmlelstv uint8_t cCode; 886dc766f2bSmlelstv uint8_t data_len; 887dc766f2bSmlelstv uint8_t *data; 888dc766f2bSmlelstv }; 889dc766f2bSmlelstv 890dc766f2bSmlelstv struct ipmi_bmc_request { 891dc766f2bSmlelstv uint8_t bmc_nfLn; 892dc766f2bSmlelstv uint8_t bmc_cmd; 893dc766f2bSmlelstv uint8_t bmc_data_len; 894dc766f2bSmlelstv uint8_t bmc_data[1]; 895dc766f2bSmlelstv }; 896dc766f2bSmlelstv 897dc766f2bSmlelstv struct ipmi_bmc_response { 898dc766f2bSmlelstv uint8_t bmc_nfLn; 899dc766f2bSmlelstv uint8_t bmc_cmd; 900dc766f2bSmlelstv uint8_t bmc_cCode; 901dc766f2bSmlelstv uint8_t bmc_data_len; 902dc766f2bSmlelstv uint8_t bmc_data[1]; 903dc766f2bSmlelstv }; 904dc766f2bSmlelstv 905dc766f2bSmlelstv 906dc766f2bSmlelstv CFATTACH_DECL2_NEW(ipmi, sizeof(struct ipmi_softc), 907dc766f2bSmlelstv ipmi_match, ipmi_attach, ipmi_detach, NULL, NULL, NULL); 908dc766f2bSmlelstv 909dc766f2bSmlelstv static void 910dc766f2bSmlelstv dumpb(const char *lbl, int len, const uint8_t *data) 911dc766f2bSmlelstv { 912dc766f2bSmlelstv int idx; 913dc766f2bSmlelstv 914dc766f2bSmlelstv printf("%s: ", lbl); 915dc766f2bSmlelstv for (idx = 0; idx < len; idx++) 916dc766f2bSmlelstv printf("%.2x ", data[idx]); 917dc766f2bSmlelstv 918dc766f2bSmlelstv printf("\n"); 919dc766f2bSmlelstv } 920dc766f2bSmlelstv 921dc766f2bSmlelstv /* 922dc766f2bSmlelstv * bt_buildmsg builds an IPMI message from a nfLun, cmd, and data 923dc766f2bSmlelstv * This is used by BT protocol 924dc766f2bSmlelstv * 925dc766f2bSmlelstv * Returns a buffer to an allocated message, txlen contains length 926dc766f2bSmlelstv * of allocated message 927dc766f2bSmlelstv */ 928dc766f2bSmlelstv static void * 929dc766f2bSmlelstv bt_buildmsg(struct ipmi_softc *sc, int nfLun, int cmd, int len, 930dc766f2bSmlelstv const void *data, int *txlen) 931dc766f2bSmlelstv { 932dc766f2bSmlelstv uint8_t *buf; 933dc766f2bSmlelstv 934dc766f2bSmlelstv /* Block transfer needs 4 extra bytes: length/netfn/seq/cmd + data */ 935dc766f2bSmlelstv *txlen = len + 4; 936dc766f2bSmlelstv buf = ipmi_buf_acquire(sc, *txlen); 937dc766f2bSmlelstv if (buf == NULL) 938dc766f2bSmlelstv return NULL; 939dc766f2bSmlelstv 940dc766f2bSmlelstv buf[IPMI_BTMSG_LEN] = len + 3; 941dc766f2bSmlelstv buf[IPMI_BTMSG_NFLN] = nfLun; 942dc766f2bSmlelstv buf[IPMI_BTMSG_SEQ] = sc->sc_btseq++; 943dc766f2bSmlelstv buf[IPMI_BTMSG_CMD] = cmd; 944dc766f2bSmlelstv if (len && data) 945dc766f2bSmlelstv memcpy(buf + IPMI_BTMSG_DATASND, data, len); 946dc766f2bSmlelstv 947dc766f2bSmlelstv return buf; 948dc766f2bSmlelstv } 949dc766f2bSmlelstv 950dc766f2bSmlelstv /* 951dc766f2bSmlelstv * cmn_buildmsg builds an IPMI message from a nfLun, cmd, and data 952dc766f2bSmlelstv * This is used by both SMIC and KCS protocols 953dc766f2bSmlelstv * 954dc766f2bSmlelstv * Returns a buffer to an allocated message, txlen contains length 955dc766f2bSmlelstv * of allocated message 956dc766f2bSmlelstv */ 957dc766f2bSmlelstv static void * 958dc766f2bSmlelstv cmn_buildmsg(struct ipmi_softc *sc, int nfLun, int cmd, int len, 959dc766f2bSmlelstv const void *data, int *txlen) 960dc766f2bSmlelstv { 961dc766f2bSmlelstv uint8_t *buf; 962dc766f2bSmlelstv 963dc766f2bSmlelstv /* Common needs two extra bytes: nfLun/cmd + data */ 964dc766f2bSmlelstv *txlen = len + 2; 965dc766f2bSmlelstv buf = ipmi_buf_acquire(sc, *txlen); 966dc766f2bSmlelstv if (buf == NULL) 967dc766f2bSmlelstv return NULL; 968dc766f2bSmlelstv 969dc766f2bSmlelstv buf[IPMI_MSG_NFLN] = nfLun; 970dc766f2bSmlelstv buf[IPMI_MSG_CMD] = cmd; 971dc766f2bSmlelstv if (len && data) 972dc766f2bSmlelstv memcpy(buf + IPMI_MSG_DATASND, data, len); 973dc766f2bSmlelstv 974dc766f2bSmlelstv return buf; 975dc766f2bSmlelstv } 976dc766f2bSmlelstv 977dc766f2bSmlelstv /* 978dc766f2bSmlelstv * ipmi_sendcmd: caller must hold sc_cmd_mtx. 979dc766f2bSmlelstv * 980dc766f2bSmlelstv * Send an IPMI command 981dc766f2bSmlelstv */ 982dc766f2bSmlelstv static int 983dc766f2bSmlelstv ipmi_sendcmd(struct ipmi_softc *sc, int rssa, int rslun, int netfn, int cmd, 984dc766f2bSmlelstv int txlen, const void *data) 985dc766f2bSmlelstv { 986dc766f2bSmlelstv uint8_t *buf; 987dc766f2bSmlelstv int rc = -1; 988dc766f2bSmlelstv 989dc766f2bSmlelstv dbg_printf(50, "%s: rssa=%#.2x nfln=%#.2x cmd=%#.2x len=%#.2x\n", 990dc766f2bSmlelstv __func__, rssa, NETFN_LUN(netfn, rslun), cmd, txlen); 991dc766f2bSmlelstv dbg_dump(10, __func__, txlen, data); 992dc766f2bSmlelstv if (rssa != BMC_SA) { 993dc766f2bSmlelstv #if 0 994dc766f2bSmlelstv buf = sc->sc_if->buildmsg(sc, NETFN_LUN(APP_NETFN, BMC_LUN), 995dc766f2bSmlelstv APP_SEND_MESSAGE, 7 + txlen, NULL, &txlen); 996dc766f2bSmlelstv pI2C->bus = (sc->if_ver == 0x09) ? 997dc766f2bSmlelstv PUBLIC_BUS : 998dc766f2bSmlelstv IPMB_CHANNEL_NUMBER; 999dc766f2bSmlelstv 1000dc766f2bSmlelstv imbreq->rsSa = rssa; 1001dc766f2bSmlelstv imbreq->nfLn = NETFN_LUN(netfn, rslun); 1002dc766f2bSmlelstv imbreq->cSum1 = -(imbreq->rsSa + imbreq->nfLn); 1003dc766f2bSmlelstv imbreq->rqSa = BMC_SA; 1004dc766f2bSmlelstv imbreq->seqLn = NETFN_LUN(sc->imb_seq++, SMS_LUN); 1005dc766f2bSmlelstv imbreq->cmd = cmd; 1006dc766f2bSmlelstv if (txlen) 1007dc766f2bSmlelstv memcpy(imbreq->data, data, txlen); 1008dc766f2bSmlelstv /* Set message checksum */ 1009dc766f2bSmlelstv imbreq->data[txlen] = cksum8(&imbreq->rqSa, txlen + 3); 1010dc766f2bSmlelstv #endif 1011dc766f2bSmlelstv goto done; 1012dc766f2bSmlelstv } else 1013dc766f2bSmlelstv buf = sc->sc_if->buildmsg(sc, NETFN_LUN(netfn, rslun), cmd, 1014dc766f2bSmlelstv txlen, data, &txlen); 1015dc766f2bSmlelstv 1016dc766f2bSmlelstv if (buf == NULL) { 1017dc766f2bSmlelstv aprint_error_dev(sc->sc_dev, "sendcmd buffer busy\n"); 1018dc766f2bSmlelstv goto done; 1019dc766f2bSmlelstv } 1020dc766f2bSmlelstv rc = sc->sc_if->sendmsg(sc, txlen, buf); 1021dc766f2bSmlelstv ipmi_buf_release(sc, buf); 1022dc766f2bSmlelstv 1023dc766f2bSmlelstv ipmi_delay(sc, 50); /* give bmc chance to digest command */ 1024dc766f2bSmlelstv 1025dc766f2bSmlelstv done: 1026dc766f2bSmlelstv return rc; 1027dc766f2bSmlelstv } 1028dc766f2bSmlelstv 1029dc766f2bSmlelstv static void 1030dc766f2bSmlelstv ipmi_buf_release(struct ipmi_softc *sc, char *buf) 1031dc766f2bSmlelstv { 1032dc766f2bSmlelstv KASSERT(sc->sc_buf_rsvd); 1033dc766f2bSmlelstv KASSERT(sc->sc_buf == buf); 1034dc766f2bSmlelstv sc->sc_buf_rsvd = false; 1035dc766f2bSmlelstv } 1036dc766f2bSmlelstv 1037dc766f2bSmlelstv static char * 1038dc766f2bSmlelstv ipmi_buf_acquire(struct ipmi_softc *sc, size_t len) 1039dc766f2bSmlelstv { 1040dc766f2bSmlelstv KASSERT(len <= sizeof(sc->sc_buf)); 1041dc766f2bSmlelstv 1042dc766f2bSmlelstv if (sc->sc_buf_rsvd || len > sizeof(sc->sc_buf)) 1043dc766f2bSmlelstv return NULL; 1044dc766f2bSmlelstv sc->sc_buf_rsvd = true; 1045dc766f2bSmlelstv return sc->sc_buf; 1046dc766f2bSmlelstv } 1047dc766f2bSmlelstv 1048dc766f2bSmlelstv /* 1049dc766f2bSmlelstv * ipmi_recvcmd: caller must hold sc_cmd_mtx. 1050dc766f2bSmlelstv */ 1051dc766f2bSmlelstv static int 1052dc766f2bSmlelstv ipmi_recvcmd(struct ipmi_softc *sc, int maxlen, int *rxlen, void *data) 1053dc766f2bSmlelstv { 1054dc766f2bSmlelstv uint8_t *buf, rc = 0; 1055dc766f2bSmlelstv int rawlen; 1056dc766f2bSmlelstv 1057dc766f2bSmlelstv /* Need three extra bytes: netfn/cmd/ccode + data */ 1058dc766f2bSmlelstv buf = ipmi_buf_acquire(sc, maxlen + 3); 1059dc766f2bSmlelstv if (buf == NULL) { 1060dc766f2bSmlelstv aprint_error_dev(sc->sc_dev, "%s: malloc fails\n", __func__); 1061dc766f2bSmlelstv return -1; 1062dc766f2bSmlelstv } 1063dc766f2bSmlelstv /* Receive message from interface, copy out result data */ 1064dc766f2bSmlelstv if (sc->sc_if->recvmsg(sc, maxlen + 3, &rawlen, buf)) { 1065dc766f2bSmlelstv ipmi_buf_release(sc, buf); 1066dc766f2bSmlelstv return -1; 1067dc766f2bSmlelstv } 1068dc766f2bSmlelstv 1069b1f23fc9Smlelstv *rxlen = rawlen >= IPMI_MSG_DATARCV ? rawlen - IPMI_MSG_DATARCV : 0; 1070dc766f2bSmlelstv if (*rxlen > 0 && data) 1071dc766f2bSmlelstv memcpy(data, buf + IPMI_MSG_DATARCV, *rxlen); 1072dc766f2bSmlelstv 1073dc766f2bSmlelstv if ((rc = buf[IPMI_MSG_CCODE]) != 0) 1074dc766f2bSmlelstv dbg_printf(1, "%s: nfln=%#.2x cmd=%#.2x err=%#.2x\n", __func__, 1075dc766f2bSmlelstv buf[IPMI_MSG_NFLN], buf[IPMI_MSG_CMD], buf[IPMI_MSG_CCODE]); 1076dc766f2bSmlelstv 1077dc766f2bSmlelstv dbg_printf(50, "%s: nfln=%#.2x cmd=%#.2x err=%#.2x len=%#.2x\n", 1078dc766f2bSmlelstv __func__, buf[IPMI_MSG_NFLN], buf[IPMI_MSG_CMD], 1079dc766f2bSmlelstv buf[IPMI_MSG_CCODE], *rxlen); 1080dc766f2bSmlelstv dbg_dump(10, __func__, *rxlen, data); 1081dc766f2bSmlelstv 1082dc766f2bSmlelstv ipmi_buf_release(sc, buf); 1083dc766f2bSmlelstv 1084dc766f2bSmlelstv return rc; 1085dc766f2bSmlelstv } 1086dc766f2bSmlelstv 1087dc766f2bSmlelstv /* 1088dc766f2bSmlelstv * ipmi_delay: caller must hold sc_cmd_mtx. 1089dc766f2bSmlelstv */ 1090dc766f2bSmlelstv static void 1091dc766f2bSmlelstv ipmi_delay(struct ipmi_softc *sc, int ms) 1092dc766f2bSmlelstv { 1093dc766f2bSmlelstv if (cold) { 1094dc766f2bSmlelstv delay(ms * 1000); 1095dc766f2bSmlelstv return; 1096dc766f2bSmlelstv } 10972ec61c4fSriastradh kpause("ipmicmd", /*intr*/false, /*timo*/mstohz(ms), /*mtx*/NULL); 1098dc766f2bSmlelstv } 1099dc766f2bSmlelstv 1100dc766f2bSmlelstv /* Read a partial SDR entry */ 1101dc766f2bSmlelstv static int 1102dc766f2bSmlelstv get_sdr_partial(struct ipmi_softc *sc, uint16_t recordId, uint16_t reserveId, 1103dc766f2bSmlelstv uint8_t offset, uint8_t length, void *buffer, uint16_t *nxtRecordId) 1104dc766f2bSmlelstv { 1105d3d86aceSriastradh union { 1106d3d86aceSriastradh struct { 1107d3d86aceSriastradh uint16_t reserveId; 1108d3d86aceSriastradh uint16_t recordId; 1109d3d86aceSriastradh uint8_t offset; 1110d3d86aceSriastradh uint8_t length; 1111d3d86aceSriastradh } __packed cmd; 1112d3d86aceSriastradh struct { 1113d3d86aceSriastradh uint16_t nxtRecordId; 1114d3d86aceSriastradh uint8_t data[262]; 1115d3d86aceSriastradh } __packed msg; 1116d3d86aceSriastradh } u; 1117dc766f2bSmlelstv int len; 1118dc766f2bSmlelstv 1119d3d86aceSriastradh __CTASSERT(sizeof(u) == 256 + 8); 1120d3d86aceSriastradh __CTASSERT(sizeof(u.cmd) == 6); 1121d3d86aceSriastradh __CTASSERT(offsetof(typeof(u.msg), data) == 2); 1122d3d86aceSriastradh 1123d3d86aceSriastradh u.cmd.reserveId = reserveId; 1124d3d86aceSriastradh u.cmd.recordId = recordId; 1125d3d86aceSriastradh u.cmd.offset = offset; 1126d3d86aceSriastradh u.cmd.length = length; 1127dc766f2bSmlelstv mutex_enter(&sc->sc_cmd_mtx); 1128d3d86aceSriastradh if (ipmi_sendcmd(sc, BMC_SA, 0, STORAGE_NETFN, STORAGE_GET_SDR, 1129d3d86aceSriastradh sizeof(u.cmd), &u.cmd)) { 1130dc766f2bSmlelstv mutex_exit(&sc->sc_cmd_mtx); 1131dc766f2bSmlelstv aprint_error_dev(sc->sc_dev, "%s: sendcmd fails\n", __func__); 1132dc766f2bSmlelstv return -1; 1133dc766f2bSmlelstv } 1134d3d86aceSriastradh if (ipmi_recvcmd(sc, 8 + length, &len, &u.msg)) { 1135dc766f2bSmlelstv mutex_exit(&sc->sc_cmd_mtx); 1136dc766f2bSmlelstv aprint_error_dev(sc->sc_dev, "%s: recvcmd fails\n", __func__); 1137dc766f2bSmlelstv return -1; 1138dc766f2bSmlelstv } 1139dc766f2bSmlelstv mutex_exit(&sc->sc_cmd_mtx); 1140dc766f2bSmlelstv if (nxtRecordId) 1141d3d86aceSriastradh *nxtRecordId = u.msg.nxtRecordId; 1142d3d86aceSriastradh memcpy(buffer, u.msg.data, len - offsetof(typeof(u.msg), data)); 1143dc766f2bSmlelstv 1144dc766f2bSmlelstv return 0; 1145dc766f2bSmlelstv } 1146dc766f2bSmlelstv 1147dc766f2bSmlelstv static int maxsdrlen = 0x10; 1148dc766f2bSmlelstv 1149dc766f2bSmlelstv /* Read an entire SDR; pass to add sensor */ 1150dc766f2bSmlelstv static int 1151dc766f2bSmlelstv get_sdr(struct ipmi_softc *sc, uint16_t recid, uint16_t *nxtrec) 1152dc766f2bSmlelstv { 1153dc766f2bSmlelstv uint16_t resid = 0; 1154dc766f2bSmlelstv int len, sdrlen, offset; 1155dc766f2bSmlelstv uint8_t *psdr; 1156dc766f2bSmlelstv struct sdrhdr shdr; 1157dc766f2bSmlelstv 1158dc766f2bSmlelstv mutex_enter(&sc->sc_cmd_mtx); 1159dc766f2bSmlelstv /* Reserve SDR */ 1160dc766f2bSmlelstv if (ipmi_sendcmd(sc, BMC_SA, 0, STORAGE_NETFN, STORAGE_RESERVE_SDR, 1161dc766f2bSmlelstv 0, NULL)) { 1162dc766f2bSmlelstv mutex_exit(&sc->sc_cmd_mtx); 1163dc766f2bSmlelstv aprint_error_dev(sc->sc_dev, "reserve send fails\n"); 1164dc766f2bSmlelstv return -1; 1165dc766f2bSmlelstv } 1166dc766f2bSmlelstv if (ipmi_recvcmd(sc, sizeof(resid), &len, &resid)) { 1167dc766f2bSmlelstv mutex_exit(&sc->sc_cmd_mtx); 1168dc766f2bSmlelstv aprint_error_dev(sc->sc_dev, "reserve recv fails\n"); 1169dc766f2bSmlelstv return -1; 1170dc766f2bSmlelstv } 1171dc766f2bSmlelstv mutex_exit(&sc->sc_cmd_mtx); 1172dc766f2bSmlelstv /* Get SDR Header */ 1173dc766f2bSmlelstv if (get_sdr_partial(sc, recid, resid, 0, sizeof shdr, &shdr, nxtrec)) { 1174dc766f2bSmlelstv aprint_error_dev(sc->sc_dev, "get header fails\n"); 1175dc766f2bSmlelstv return -1; 1176dc766f2bSmlelstv } 1177dc766f2bSmlelstv /* Allocate space for entire SDR Length of SDR in header does not 1178dc766f2bSmlelstv * include header length */ 1179dc766f2bSmlelstv sdrlen = sizeof(shdr) + shdr.record_length; 1180dc766f2bSmlelstv psdr = malloc(sdrlen, M_DEVBUF, M_WAITOK); 1181dc766f2bSmlelstv if (psdr == NULL) 1182dc766f2bSmlelstv return -1; 1183dc766f2bSmlelstv 1184dc766f2bSmlelstv memcpy(psdr, &shdr, sizeof(shdr)); 1185dc766f2bSmlelstv 1186dc766f2bSmlelstv /* Read SDR Data maxsdrlen bytes at a time */ 1187dc766f2bSmlelstv for (offset = sizeof(shdr); offset < sdrlen; offset += maxsdrlen) { 1188dc766f2bSmlelstv len = sdrlen - offset; 1189dc766f2bSmlelstv if (len > maxsdrlen) 1190dc766f2bSmlelstv len = maxsdrlen; 1191dc766f2bSmlelstv 1192dc766f2bSmlelstv if (get_sdr_partial(sc, recid, resid, offset, len, 1193dc766f2bSmlelstv psdr + offset, NULL)) { 1194dc766f2bSmlelstv aprint_error_dev(sc->sc_dev, 1195dc766f2bSmlelstv "get chunk : %d,%d fails\n", offset, len); 1196dc766f2bSmlelstv free(psdr, M_DEVBUF); 1197dc766f2bSmlelstv return -1; 1198dc766f2bSmlelstv } 1199dc766f2bSmlelstv } 1200dc766f2bSmlelstv 1201dc766f2bSmlelstv /* Add SDR to sensor list, if not wanted, free buffer */ 1202dc766f2bSmlelstv if (add_sdr_sensor(sc, psdr) == 0) 1203dc766f2bSmlelstv free(psdr, M_DEVBUF); 1204dc766f2bSmlelstv 1205dc766f2bSmlelstv return 0; 1206dc766f2bSmlelstv } 1207dc766f2bSmlelstv 1208dc766f2bSmlelstv static int 1209dc766f2bSmlelstv getbits(uint8_t *bytes, int bitpos, int bitlen) 1210dc766f2bSmlelstv { 1211dc766f2bSmlelstv int v; 1212dc766f2bSmlelstv int mask; 1213dc766f2bSmlelstv 1214dc766f2bSmlelstv bitpos += bitlen - 1; 1215dc766f2bSmlelstv for (v = 0; bitlen--;) { 1216dc766f2bSmlelstv v <<= 1; 1217dc766f2bSmlelstv mask = 1L << (bitpos & 7); 1218dc766f2bSmlelstv if (bytes[bitpos >> 3] & mask) 1219dc766f2bSmlelstv v |= 1; 1220dc766f2bSmlelstv bitpos--; 1221dc766f2bSmlelstv } 1222dc766f2bSmlelstv 1223dc766f2bSmlelstv return v; 1224dc766f2bSmlelstv } 1225dc766f2bSmlelstv 1226dc766f2bSmlelstv /* Decode IPMI sensor name */ 1227dc766f2bSmlelstv static void 1228dc766f2bSmlelstv ipmi_sensor_name(char *name, int len, uint8_t typelen, uint8_t *bits) 1229dc766f2bSmlelstv { 1230dc766f2bSmlelstv int i, slen; 1231dc766f2bSmlelstv char bcdplus[] = "0123456789 -.:,_"; 1232dc766f2bSmlelstv 1233dc766f2bSmlelstv slen = typelen & 0x1F; 1234dc766f2bSmlelstv switch (typelen >> 6) { 1235dc766f2bSmlelstv case IPMI_NAME_UNICODE: 1236dc766f2bSmlelstv //unicode 1237dc766f2bSmlelstv break; 1238dc766f2bSmlelstv 1239dc766f2bSmlelstv case IPMI_NAME_BCDPLUS: 1240dc766f2bSmlelstv /* Characters are encoded in 4-bit BCDPLUS */ 1241dc766f2bSmlelstv if (len < slen * 2 + 1) 1242dc766f2bSmlelstv slen = (len >> 1) - 1; 1243dc766f2bSmlelstv for (i = 0; i < slen; i++) { 1244dc766f2bSmlelstv *(name++) = bcdplus[bits[i] >> 4]; 1245dc766f2bSmlelstv *(name++) = bcdplus[bits[i] & 0xF]; 1246dc766f2bSmlelstv } 1247dc766f2bSmlelstv break; 1248dc766f2bSmlelstv 1249dc766f2bSmlelstv case IPMI_NAME_ASCII6BIT: 1250dc766f2bSmlelstv /* Characters are encoded in 6-bit ASCII 1251dc766f2bSmlelstv * 0x00 - 0x3F maps to 0x20 - 0x5F */ 1252dc766f2bSmlelstv /* XXX: need to calculate max len: slen = 3/4 * len */ 1253dc766f2bSmlelstv if (len < slen + 1) 1254dc766f2bSmlelstv slen = len - 1; 1255dc766f2bSmlelstv for (i = 0; i < slen * 8; i += 6) 1256dc766f2bSmlelstv *(name++) = getbits(bits, i, 6) + ' '; 1257dc766f2bSmlelstv break; 1258dc766f2bSmlelstv 1259dc766f2bSmlelstv case IPMI_NAME_ASCII8BIT: 1260dc766f2bSmlelstv /* Characters are 8-bit ascii */ 1261dc766f2bSmlelstv if (len < slen + 1) 1262dc766f2bSmlelstv slen = len - 1; 1263dc766f2bSmlelstv while (slen--) 1264dc766f2bSmlelstv *(name++) = *(bits++); 1265dc766f2bSmlelstv break; 1266dc766f2bSmlelstv } 1267dc766f2bSmlelstv *name = 0; 1268dc766f2bSmlelstv } 1269dc766f2bSmlelstv 1270dc766f2bSmlelstv /* Sign extend a n-bit value */ 1271dc766f2bSmlelstv static long 1272dc766f2bSmlelstv signextend(unsigned long val, int bits) 1273dc766f2bSmlelstv { 1274dc766f2bSmlelstv long msk = (1L << (bits-1))-1; 1275dc766f2bSmlelstv 1276dc766f2bSmlelstv return -(val & ~msk) | val; 1277dc766f2bSmlelstv } 1278dc766f2bSmlelstv 1279dc766f2bSmlelstv 1280dc766f2bSmlelstv /* fixpoint arithmetic */ 1281dc766f2bSmlelstv #define FIX2INT(x) ((int64_t)((x) >> 32)) 1282dc766f2bSmlelstv #define INT2FIX(x) ((int64_t)((uint64_t)(x) << 32)) 1283dc766f2bSmlelstv 1284dc766f2bSmlelstv #define FIX2 0x0000000200000000ll /* 2.0 */ 1285dc766f2bSmlelstv #define FIX3 0x0000000300000000ll /* 3.0 */ 1286dc766f2bSmlelstv #define FIXE 0x00000002b7e15163ll /* 2.71828182845904523536 */ 1287dc766f2bSmlelstv #define FIX10 0x0000000a00000000ll /* 10.0 */ 1288dc766f2bSmlelstv #define FIXMONE 0xffffffff00000000ll /* -1.0 */ 1289dc766f2bSmlelstv #define FIXHALF 0x0000000080000000ll /* 0.5 */ 1290dc766f2bSmlelstv #define FIXTHIRD 0x0000000055555555ll /* 0.33333333333333333333 */ 1291dc766f2bSmlelstv 1292dc766f2bSmlelstv #define FIX1LOG2 0x0000000171547653ll /* 1.0/log(2) */ 1293dc766f2bSmlelstv #define FIX1LOGE 0x0000000100000000ll /* 1.0/log(2.71828182845904523536) */ 1294dc766f2bSmlelstv #define FIX1LOG10 0x000000006F2DEC55ll /* 1.0/log(10) */ 1295dc766f2bSmlelstv 1296dc766f2bSmlelstv #define FIX1E 0x000000005E2D58D9ll /* 1.0/2.71828182845904523536 */ 1297dc766f2bSmlelstv 1298dc766f2bSmlelstv static int64_t fixlog_a[] = { 1299dc766f2bSmlelstv 0x0000000100000000ll /* 1.0/1.0 */, 1300dc766f2bSmlelstv 0xffffffff80000000ll /* -1.0/2.0 */, 1301dc766f2bSmlelstv 0x0000000055555555ll /* 1.0/3.0 */, 1302dc766f2bSmlelstv 0xffffffffc0000000ll /* -1.0/4.0 */, 1303dc766f2bSmlelstv 0x0000000033333333ll /* 1.0/5.0 */, 1304dc766f2bSmlelstv 0x000000002aaaaaabll /* -1.0/6.0 */, 1305dc766f2bSmlelstv 0x0000000024924925ll /* 1.0/7.0 */, 1306dc766f2bSmlelstv 0x0000000020000000ll /* -1.0/8.0 */, 1307dc766f2bSmlelstv 0x000000001c71c71cll /* 1.0/9.0 */ 1308dc766f2bSmlelstv }; 1309dc766f2bSmlelstv 1310dc766f2bSmlelstv static int64_t fixexp_a[] = { 1311dc766f2bSmlelstv 0x0000000100000000ll /* 1.0/1.0 */, 1312dc766f2bSmlelstv 0x0000000100000000ll /* 1.0/1.0 */, 1313dc766f2bSmlelstv 0x0000000080000000ll /* 1.0/2.0 */, 1314dc766f2bSmlelstv 0x000000002aaaaaabll /* 1.0/6.0 */, 1315dc766f2bSmlelstv 0x000000000aaaaaabll /* 1.0/24.0 */, 1316dc766f2bSmlelstv 0x0000000002222222ll /* 1.0/120.0 */, 1317dc766f2bSmlelstv 0x00000000005b05b0ll /* 1.0/720.0 */, 1318dc766f2bSmlelstv 0x00000000000d00d0ll /* 1.0/5040.0 */, 1319dc766f2bSmlelstv 0x000000000001a01all /* 1.0/40320.0 */ 1320dc766f2bSmlelstv }; 1321dc766f2bSmlelstv 1322dc766f2bSmlelstv static int64_t 1323dc766f2bSmlelstv fixmul(int64_t x, int64_t y) 1324dc766f2bSmlelstv { 1325dc766f2bSmlelstv int64_t z; 1326dc766f2bSmlelstv int64_t a,b,c,d; 1327dc766f2bSmlelstv int neg; 1328dc766f2bSmlelstv 1329dc766f2bSmlelstv neg = 0; 1330dc766f2bSmlelstv if (x < 0) { 1331dc766f2bSmlelstv x = -x; 1332dc766f2bSmlelstv neg = !neg; 1333dc766f2bSmlelstv } 1334dc766f2bSmlelstv if (y < 0) { 1335dc766f2bSmlelstv y = -y; 1336dc766f2bSmlelstv neg = !neg; 1337dc766f2bSmlelstv } 1338dc766f2bSmlelstv 1339dc766f2bSmlelstv a = FIX2INT(x); 1340dc766f2bSmlelstv b = x - INT2FIX(a); 1341dc766f2bSmlelstv c = FIX2INT(y); 1342dc766f2bSmlelstv d = y - INT2FIX(c); 1343dc766f2bSmlelstv 1344dc766f2bSmlelstv z = INT2FIX(a*c) + a * d + b * c + (b/2 * d/2 >> 30); 1345dc766f2bSmlelstv 1346dc766f2bSmlelstv return neg ? -z : z; 1347dc766f2bSmlelstv } 1348dc766f2bSmlelstv 1349dc766f2bSmlelstv static int64_t 1350dc766f2bSmlelstv poly(int64_t x0, int64_t x, int64_t a[], int n) 1351dc766f2bSmlelstv { 1352dc766f2bSmlelstv int64_t z; 1353dc766f2bSmlelstv int i; 1354dc766f2bSmlelstv 1355dc766f2bSmlelstv z = fixmul(x0, a[0]); 1356dc766f2bSmlelstv for (i=1; i<n; ++i) { 1357dc766f2bSmlelstv x0 = fixmul(x0, x); 1358dc766f2bSmlelstv z = fixmul(x0, a[i]) + z; 1359dc766f2bSmlelstv } 1360dc766f2bSmlelstv return z; 1361dc766f2bSmlelstv } 1362dc766f2bSmlelstv 1363dc766f2bSmlelstv static int64_t 1364dc766f2bSmlelstv logx(int64_t x, int64_t y) 1365dc766f2bSmlelstv { 1366dc766f2bSmlelstv int64_t z; 1367dc766f2bSmlelstv 1368dc766f2bSmlelstv if (x <= INT2FIX(0)) { 1369dc766f2bSmlelstv z = INT2FIX(-99999); 1370dc766f2bSmlelstv goto done; 1371dc766f2bSmlelstv } 1372dc766f2bSmlelstv 1373dc766f2bSmlelstv z = INT2FIX(0); 1374dc766f2bSmlelstv while (x >= FIXE) { 1375dc766f2bSmlelstv x = fixmul(x, FIX1E); 1376dc766f2bSmlelstv z += INT2FIX(1); 1377dc766f2bSmlelstv } 1378dc766f2bSmlelstv while (x < INT2FIX(1)) { 1379dc766f2bSmlelstv x = fixmul(x, FIXE); 1380dc766f2bSmlelstv z -= INT2FIX(1); 1381dc766f2bSmlelstv } 1382dc766f2bSmlelstv 1383dc766f2bSmlelstv x -= INT2FIX(1); 1384dc766f2bSmlelstv z += poly(x, x, fixlog_a, sizeof(fixlog_a)/sizeof(fixlog_a[0])); 1385dc766f2bSmlelstv z = fixmul(z, y); 1386dc766f2bSmlelstv 1387dc766f2bSmlelstv done: 1388dc766f2bSmlelstv return z; 1389dc766f2bSmlelstv } 1390dc766f2bSmlelstv 1391dc766f2bSmlelstv static int64_t 1392dc766f2bSmlelstv powx(int64_t x, int64_t y) 1393dc766f2bSmlelstv { 1394dc766f2bSmlelstv int64_t k; 1395dc766f2bSmlelstv 1396dc766f2bSmlelstv if (x == INT2FIX(0)) 1397dc766f2bSmlelstv goto done; 1398dc766f2bSmlelstv 1399dc766f2bSmlelstv x = logx(x,y); 1400dc766f2bSmlelstv 1401dc766f2bSmlelstv if (x < INT2FIX(0)) { 1402dc766f2bSmlelstv x = INT2FIX(0) - x; 1403dc766f2bSmlelstv k = -FIX2INT(x); 1404dc766f2bSmlelstv x = INT2FIX(-k) - x; 1405dc766f2bSmlelstv } else { 1406dc766f2bSmlelstv k = FIX2INT(x); 1407dc766f2bSmlelstv x = x - INT2FIX(k); 1408dc766f2bSmlelstv } 1409dc766f2bSmlelstv 1410dc766f2bSmlelstv x = poly(INT2FIX(1), x, fixexp_a, sizeof(fixexp_a)/sizeof(fixexp_a[0])); 1411dc766f2bSmlelstv 1412dc766f2bSmlelstv while (k < 0) { 1413dc766f2bSmlelstv x = fixmul(x, FIX1E); 1414dc766f2bSmlelstv ++k; 1415dc766f2bSmlelstv } 1416dc766f2bSmlelstv while (k > 0) { 1417dc766f2bSmlelstv x = fixmul(x, FIXE); 1418dc766f2bSmlelstv --k; 1419dc766f2bSmlelstv } 1420dc766f2bSmlelstv 1421dc766f2bSmlelstv done: 1422dc766f2bSmlelstv return x; 1423dc766f2bSmlelstv } 1424dc766f2bSmlelstv 1425dc766f2bSmlelstv /* Convert IPMI reading from sensor factors */ 1426dc766f2bSmlelstv static long 1427dc766f2bSmlelstv ipmi_convert(uint8_t v, struct sdrtype1 *s1, long adj) 1428dc766f2bSmlelstv { 1429dc766f2bSmlelstv int64_t M, B; 1430dc766f2bSmlelstv char K1, K2; 1431dc766f2bSmlelstv int64_t val, v1, v2, vs; 1432dc766f2bSmlelstv int sign = (s1->units1 >> 6) & 0x3; 1433dc766f2bSmlelstv 1434dc766f2bSmlelstv vs = (sign == 0x1 || sign == 0x2) ? (int8_t)v : v; 1435dc766f2bSmlelstv if ((vs < 0) && (sign == 0x1)) 1436dc766f2bSmlelstv vs++; 1437dc766f2bSmlelstv 1438dc766f2bSmlelstv /* Calculate linear reading variables */ 1439dc766f2bSmlelstv M = signextend((((short)(s1->m_tolerance & 0xC0)) << 2) + s1->m, 10); 1440dc766f2bSmlelstv B = signextend((((short)(s1->b_accuracy & 0xC0)) << 2) + s1->b, 10); 1441dc766f2bSmlelstv K1 = signextend(s1->rbexp & 0xF, 4); 1442dc766f2bSmlelstv K2 = signextend(s1->rbexp >> 4, 4); 1443dc766f2bSmlelstv 1444dc766f2bSmlelstv /* Calculate sensor reading: 1445dc766f2bSmlelstv * y = L((M * v + (B * 10^K1)) * 10^(K2+adj) 1446dc766f2bSmlelstv * 1447dc766f2bSmlelstv * This commutes out to: 1448dc766f2bSmlelstv * y = L(M*v * 10^(K2+adj) + B * 10^(K1+K2+adj)); */ 1449dc766f2bSmlelstv v1 = powx(FIX10, INT2FIX(K2 + adj)); 1450dc766f2bSmlelstv v2 = powx(FIX10, INT2FIX(K1 + K2 + adj)); 1451dc766f2bSmlelstv val = M * vs * v1 + B * v2; 1452dc766f2bSmlelstv 1453dc766f2bSmlelstv /* Linearization function: y = f(x) 0 : y = x 1 : y = ln(x) 2 : y = 1454dc766f2bSmlelstv * log10(x) 3 : y = log2(x) 4 : y = e^x 5 : y = 10^x 6 : y = 2^x 7 : y 1455dc766f2bSmlelstv * = 1/x 8 : y = x^2 9 : y = x^3 10 : y = square root(x) 11 : y = cube 1456dc766f2bSmlelstv * root(x) */ 1457dc766f2bSmlelstv switch (s1->linear & 0x7f) { 1458dc766f2bSmlelstv case 0: break; 1459dc766f2bSmlelstv case 1: val = logx(val,FIX1LOGE); break; 1460dc766f2bSmlelstv case 2: val = logx(val,FIX1LOG10); break; 1461dc766f2bSmlelstv case 3: val = logx(val,FIX1LOG2); break; 1462dc766f2bSmlelstv case 4: val = powx(FIXE,val); break; 1463dc766f2bSmlelstv case 5: val = powx(FIX10,val); break; 1464dc766f2bSmlelstv case 6: val = powx(FIX2,val); break; 1465dc766f2bSmlelstv case 7: val = powx(val,FIXMONE); break; 1466dc766f2bSmlelstv case 8: val = powx(val,FIX2); break; 1467dc766f2bSmlelstv case 9: val = powx(val,FIX3); break; 1468dc766f2bSmlelstv case 10: val = powx(val,FIXHALF); break; 1469dc766f2bSmlelstv case 11: val = powx(val,FIXTHIRD); break; 1470dc766f2bSmlelstv } 1471dc766f2bSmlelstv 1472dc766f2bSmlelstv return FIX2INT(val); 1473dc766f2bSmlelstv } 1474dc766f2bSmlelstv 1475dc766f2bSmlelstv static int32_t 1476dc766f2bSmlelstv ipmi_convert_sensor(uint8_t *reading, struct ipmi_sensor *psensor) 1477dc766f2bSmlelstv { 1478dc766f2bSmlelstv struct sdrtype1 *s1 = (struct sdrtype1 *)psensor->i_sdr; 1479dc766f2bSmlelstv int32_t val; 1480dc766f2bSmlelstv 1481dc766f2bSmlelstv switch (psensor->i_envtype) { 1482dc766f2bSmlelstv case ENVSYS_STEMP: 1483dc766f2bSmlelstv val = ipmi_convert(reading[0], s1, 6) + 273150000; 1484dc766f2bSmlelstv break; 1485dc766f2bSmlelstv 1486dc766f2bSmlelstv case ENVSYS_SVOLTS_DC: 1487dc766f2bSmlelstv val = ipmi_convert(reading[0], s1, 6); 1488dc766f2bSmlelstv break; 1489dc766f2bSmlelstv 1490dc766f2bSmlelstv case ENVSYS_SFANRPM: 1491dc766f2bSmlelstv val = ipmi_convert(reading[0], s1, 0); 1492dc766f2bSmlelstv if (((s1->units1>>3)&0x7) == 0x3) 1493dc766f2bSmlelstv val *= 60; /* RPS -> RPM */ 1494dc766f2bSmlelstv break; 1495dc766f2bSmlelstv default: 1496dc766f2bSmlelstv val = 0; 1497dc766f2bSmlelstv break; 1498dc766f2bSmlelstv } 1499dc766f2bSmlelstv return val; 1500dc766f2bSmlelstv } 1501dc766f2bSmlelstv 1502dc766f2bSmlelstv static void 1503dc766f2bSmlelstv ipmi_set_limits(struct sysmon_envsys *sme, envsys_data_t *edata, 1504dc766f2bSmlelstv sysmon_envsys_lim_t *limits, uint32_t *props) 1505dc766f2bSmlelstv { 1506dc766f2bSmlelstv struct ipmi_sensor *ipmi_s; 1507dc766f2bSmlelstv 1508dc766f2bSmlelstv /* Find the ipmi_sensor corresponding to this edata */ 1509dc766f2bSmlelstv SLIST_FOREACH(ipmi_s, &ipmi_sensor_list, i_list) { 1510dc766f2bSmlelstv if (ipmi_s->i_envnum == edata->sensor) { 1511dc766f2bSmlelstv if (limits == NULL) { 1512dc766f2bSmlelstv limits = &ipmi_s->i_deflims; 1513dc766f2bSmlelstv props = &ipmi_s->i_defprops; 1514dc766f2bSmlelstv } 1515dc766f2bSmlelstv *props |= PROP_DRIVER_LIMITS; 1516dc766f2bSmlelstv ipmi_s->i_limits = *limits; 1517dc766f2bSmlelstv ipmi_s->i_props = *props; 1518dc766f2bSmlelstv return; 1519dc766f2bSmlelstv } 1520dc766f2bSmlelstv } 1521dc766f2bSmlelstv return; 1522dc766f2bSmlelstv } 1523dc766f2bSmlelstv 1524dc766f2bSmlelstv static void 1525dc766f2bSmlelstv ipmi_get_limits(struct sysmon_envsys *sme, envsys_data_t *edata, 1526dc766f2bSmlelstv sysmon_envsys_lim_t *limits, uint32_t *props) 1527dc766f2bSmlelstv { 1528dc766f2bSmlelstv struct ipmi_sensor *ipmi_s; 1529dc766f2bSmlelstv struct ipmi_softc *sc = sme->sme_cookie; 1530dc766f2bSmlelstv 1531dc766f2bSmlelstv /* Find the ipmi_sensor corresponding to this edata */ 1532dc766f2bSmlelstv SLIST_FOREACH(ipmi_s, &ipmi_sensor_list, i_list) { 1533dc766f2bSmlelstv if (ipmi_s->i_envnum == edata->sensor) { 1534dc766f2bSmlelstv ipmi_get_sensor_limits(sc, ipmi_s, limits, props); 1535dc766f2bSmlelstv ipmi_s->i_limits = *limits; 1536dc766f2bSmlelstv ipmi_s->i_props = *props; 1537dc766f2bSmlelstv if (ipmi_s->i_defprops == 0) { 1538dc766f2bSmlelstv ipmi_s->i_defprops = *props; 1539dc766f2bSmlelstv ipmi_s->i_deflims = *limits; 1540dc766f2bSmlelstv } 1541dc766f2bSmlelstv return; 1542dc766f2bSmlelstv } 1543dc766f2bSmlelstv } 1544dc766f2bSmlelstv return; 1545dc766f2bSmlelstv } 1546dc766f2bSmlelstv 1547404b3312Smlelstv /* valid bits for (upper,lower) x (non-recoverable, critical, warn) */ 1548404b3312Smlelstv #define UN 0x20 1549404b3312Smlelstv #define UC 0x10 1550404b3312Smlelstv #define UW 0x08 1551404b3312Smlelstv #define LN 0x04 1552404b3312Smlelstv #define LC 0x02 1553404b3312Smlelstv #define LW 0x01 1554404b3312Smlelstv 1555dc766f2bSmlelstv static void 1556dc766f2bSmlelstv ipmi_get_sensor_limits(struct ipmi_softc *sc, struct ipmi_sensor *psensor, 1557dc766f2bSmlelstv sysmon_envsys_lim_t *limits, uint32_t *props) 1558dc766f2bSmlelstv { 1559dc766f2bSmlelstv struct sdrtype1 *s1 = (struct sdrtype1 *)psensor->i_sdr; 1560dc766f2bSmlelstv bool failure; 1561dc766f2bSmlelstv int rxlen; 1562404b3312Smlelstv uint8_t data[32], valid; 1563dc766f2bSmlelstv uint32_t prop_critmax, prop_warnmax, prop_critmin, prop_warnmin; 1564dc766f2bSmlelstv int32_t *pcritmax, *pwarnmax, *pcritmin, *pwarnmin; 1565dc766f2bSmlelstv 1566dc766f2bSmlelstv *props &= ~(PROP_CRITMIN | PROP_CRITMAX | PROP_WARNMIN | PROP_WARNMAX); 1567dc766f2bSmlelstv data[0] = psensor->i_num; 1568dc766f2bSmlelstv mutex_enter(&sc->sc_cmd_mtx); 1569dc766f2bSmlelstv failure = 1570dc766f2bSmlelstv ipmi_sendcmd(sc, s1->owner_id, s1->owner_lun, 1571dc766f2bSmlelstv SE_NETFN, SE_GET_SENSOR_THRESHOLD, 1, data) || 1572dc766f2bSmlelstv ipmi_recvcmd(sc, sizeof(data), &rxlen, data); 1573dc766f2bSmlelstv mutex_exit(&sc->sc_cmd_mtx); 1574dc766f2bSmlelstv if (failure) 1575dc766f2bSmlelstv return; 1576dc766f2bSmlelstv 1577dc766f2bSmlelstv dbg_printf(25, "%s: %#.2x %#.2x %#.2x %#.2x %#.2x %#.2x %#.2x\n", 1578dc766f2bSmlelstv __func__, data[0], data[1], data[2], data[3], data[4], data[5], 1579dc766f2bSmlelstv data[6]); 1580dc766f2bSmlelstv 1581dc766f2bSmlelstv switch (s1->linear & 0x7f) { 1582dc766f2bSmlelstv case 7: /* 1/x sensor, exchange upper and lower limits */ 1583dc766f2bSmlelstv prop_critmax = PROP_CRITMIN; 1584dc766f2bSmlelstv prop_warnmax = PROP_WARNMIN; 1585dc766f2bSmlelstv prop_critmin = PROP_CRITMAX; 1586dc766f2bSmlelstv prop_warnmin = PROP_WARNMAX; 1587dc766f2bSmlelstv pcritmax = &limits->sel_critmin; 1588dc766f2bSmlelstv pwarnmax = &limits->sel_warnmin; 1589dc766f2bSmlelstv pcritmin = &limits->sel_critmax; 1590dc766f2bSmlelstv pwarnmin = &limits->sel_warnmax; 1591dc766f2bSmlelstv break; 1592dc766f2bSmlelstv default: 1593dc766f2bSmlelstv prop_critmax = PROP_CRITMAX; 1594dc766f2bSmlelstv prop_warnmax = PROP_WARNMAX; 1595dc766f2bSmlelstv prop_critmin = PROP_CRITMIN; 1596dc766f2bSmlelstv prop_warnmin = PROP_WARNMIN; 1597dc766f2bSmlelstv pcritmax = &limits->sel_critmax; 1598dc766f2bSmlelstv pwarnmax = &limits->sel_warnmax; 1599dc766f2bSmlelstv pcritmin = &limits->sel_critmin; 1600dc766f2bSmlelstv pwarnmin = &limits->sel_warnmin; 1601dc766f2bSmlelstv break; 1602dc766f2bSmlelstv } 1603dc766f2bSmlelstv 1604404b3312Smlelstv valid = data[0]; 1605404b3312Smlelstv 1606404b3312Smlelstv /* if upper non-recoverable < warning, ignore it */ 1607404b3312Smlelstv if ((valid & (UN|UW)) == (UN|UW) && data[6] < data[4]) 1608404b3312Smlelstv valid ^= UN; 1609404b3312Smlelstv /* if upper critical < warning, ignore it */ 1610404b3312Smlelstv if ((valid & (UC|UW)) == (UC|UW) && data[5] < data[4]) 1611404b3312Smlelstv valid ^= UC; 1612404b3312Smlelstv 1613404b3312Smlelstv /* if lower non-recoverable > warning, ignore it */ 1614404b3312Smlelstv if ((data[0] & (LN|LW)) == (LN|LW) && data[3] > data[1]) 1615404b3312Smlelstv valid ^= LN; 1616404b3312Smlelstv /* if lower critical > warning, ignore it */ 1617404b3312Smlelstv if ((data[0] & (LC|LW)) == (LC|LW) && data[2] > data[1]) 1618404b3312Smlelstv valid ^= LC; 1619404b3312Smlelstv 1620404b3312Smlelstv if (valid & UN && data[6] != 0xff) { 1621dc766f2bSmlelstv *pcritmax = ipmi_convert_sensor(&data[6], psensor); 1622dc766f2bSmlelstv *props |= prop_critmax; 1623dc766f2bSmlelstv } 1624404b3312Smlelstv if (valid & UC && data[5] != 0xff) { 1625dc766f2bSmlelstv *pcritmax = ipmi_convert_sensor(&data[5], psensor); 1626dc766f2bSmlelstv *props |= prop_critmax; 1627dc766f2bSmlelstv } 1628404b3312Smlelstv if (valid & UW && data[4] != 0xff) { 1629dc766f2bSmlelstv *pwarnmax = ipmi_convert_sensor(&data[4], psensor); 1630dc766f2bSmlelstv *props |= prop_warnmax; 1631dc766f2bSmlelstv } 1632404b3312Smlelstv if (valid & LN && data[3] != 0x00) { 1633dc766f2bSmlelstv *pcritmin = ipmi_convert_sensor(&data[3], psensor); 1634dc766f2bSmlelstv *props |= prop_critmin; 1635dc766f2bSmlelstv } 1636404b3312Smlelstv if (valid & LC && data[2] != 0x00) { 1637dc766f2bSmlelstv *pcritmin = ipmi_convert_sensor(&data[2], psensor); 1638dc766f2bSmlelstv *props |= prop_critmin; 1639dc766f2bSmlelstv } 1640404b3312Smlelstv if (valid & LW && data[1] != 0x00) { 1641dc766f2bSmlelstv *pwarnmin = ipmi_convert_sensor(&data[1], psensor); 1642dc766f2bSmlelstv *props |= prop_warnmin; 1643dc766f2bSmlelstv } 1644dc766f2bSmlelstv return; 1645dc766f2bSmlelstv } 1646dc766f2bSmlelstv 1647dc766f2bSmlelstv static int 1648dc766f2bSmlelstv ipmi_sensor_status(struct ipmi_softc *sc, struct ipmi_sensor *psensor, 1649dc766f2bSmlelstv envsys_data_t *edata, uint8_t *reading) 1650dc766f2bSmlelstv { 1651dc766f2bSmlelstv int etype; 1652dc766f2bSmlelstv 1653dc766f2bSmlelstv /* Get reading of sensor */ 1654dc766f2bSmlelstv edata->value_cur = ipmi_convert_sensor(reading, psensor); 1655dc766f2bSmlelstv 1656dc766f2bSmlelstv /* Return Sensor Status */ 1657dc766f2bSmlelstv etype = (psensor->i_etype << 8) + psensor->i_stype; 1658dc766f2bSmlelstv switch (etype) { 1659dc766f2bSmlelstv case IPMI_SENSOR_TYPE_TEMP: 1660dc766f2bSmlelstv case IPMI_SENSOR_TYPE_VOLT: 1661dc766f2bSmlelstv case IPMI_SENSOR_TYPE_FAN: 1662dc766f2bSmlelstv if (psensor->i_props & PROP_CRITMAX && 1663dc766f2bSmlelstv edata->value_cur > psensor->i_limits.sel_critmax) 1664dc766f2bSmlelstv return ENVSYS_SCRITOVER; 1665dc766f2bSmlelstv 1666dc766f2bSmlelstv if (psensor->i_props & PROP_WARNMAX && 1667dc766f2bSmlelstv edata->value_cur > psensor->i_limits.sel_warnmax) 1668dc766f2bSmlelstv return ENVSYS_SWARNOVER; 1669dc766f2bSmlelstv 1670dc766f2bSmlelstv if (psensor->i_props & PROP_CRITMIN && 1671dc766f2bSmlelstv edata->value_cur < psensor->i_limits.sel_critmin) 1672dc766f2bSmlelstv return ENVSYS_SCRITUNDER; 1673dc766f2bSmlelstv 16747e1880b3Snonaka if (psensor->i_props & PROP_WARNMIN && 16757e1880b3Snonaka edata->value_cur < psensor->i_limits.sel_warnmin) 16767e1880b3Snonaka return ENVSYS_SWARNUNDER; 16777e1880b3Snonaka 1678dc766f2bSmlelstv break; 1679dc766f2bSmlelstv 1680dc766f2bSmlelstv case IPMI_SENSOR_TYPE_INTRUSION: 1681dc766f2bSmlelstv edata->value_cur = (reading[2] & 1) ? 0 : 1; 1682dc766f2bSmlelstv if (reading[2] & 0x1) 1683dc766f2bSmlelstv return ENVSYS_SCRITICAL; 1684dc766f2bSmlelstv break; 1685dc766f2bSmlelstv 1686dc766f2bSmlelstv case IPMI_SENSOR_TYPE_PWRSUPPLY: 1687dc766f2bSmlelstv /* Reading: 1 = present+powered, 0 = otherwise */ 1688dc766f2bSmlelstv edata->value_cur = (reading[2] & 1) ? 0 : 1; 1689dc766f2bSmlelstv if (reading[2] & 0x10) { 1690dc766f2bSmlelstv /* XXX: Need envsys type for Power Supply types 1691dc766f2bSmlelstv * ok: power supply installed && powered 1692dc766f2bSmlelstv * warn: power supply installed && !powered 1693dc766f2bSmlelstv * crit: power supply !installed 1694dc766f2bSmlelstv */ 1695dc766f2bSmlelstv return ENVSYS_SCRITICAL; 1696dc766f2bSmlelstv } 1697dc766f2bSmlelstv if (reading[2] & 0x08) { 1698dc766f2bSmlelstv /* Power supply AC lost */ 1699dc766f2bSmlelstv return ENVSYS_SWARNOVER; 1700dc766f2bSmlelstv } 1701dc766f2bSmlelstv break; 1702dc766f2bSmlelstv } 1703dc766f2bSmlelstv 1704dc766f2bSmlelstv return ENVSYS_SVALID; 1705dc766f2bSmlelstv } 1706dc766f2bSmlelstv 1707dc766f2bSmlelstv static int 1708dc766f2bSmlelstv read_sensor(struct ipmi_softc *sc, struct ipmi_sensor *psensor) 1709dc766f2bSmlelstv { 1710dc766f2bSmlelstv struct sdrtype1 *s1 = (struct sdrtype1 *) psensor->i_sdr; 1711dc766f2bSmlelstv uint8_t data[8]; 1712dc766f2bSmlelstv int rxlen; 1713dc766f2bSmlelstv envsys_data_t *edata = &sc->sc_sensor[psensor->i_envnum]; 1714dc766f2bSmlelstv 1715dc766f2bSmlelstv memset(data, 0, sizeof(data)); 1716dc766f2bSmlelstv data[0] = psensor->i_num; 1717dc766f2bSmlelstv 1718dc766f2bSmlelstv mutex_enter(&sc->sc_cmd_mtx); 1719dc766f2bSmlelstv if (ipmi_sendcmd(sc, s1->owner_id, s1->owner_lun, SE_NETFN, 1720dc766f2bSmlelstv SE_GET_SENSOR_READING, 1, data)) 1721dc766f2bSmlelstv goto err; 1722dc766f2bSmlelstv 1723dc766f2bSmlelstv if (ipmi_recvcmd(sc, sizeof(data), &rxlen, data)) 1724dc766f2bSmlelstv goto err; 1725dc766f2bSmlelstv mutex_exit(&sc->sc_cmd_mtx); 1726dc766f2bSmlelstv 1727dc766f2bSmlelstv dbg_printf(10, "m=%u, m_tolerance=%u, b=%u, b_accuracy=%u, " 1728dc766f2bSmlelstv "rbexp=%u, linear=%d\n", s1->m, s1->m_tolerance, s1->b, 1729dc766f2bSmlelstv s1->b_accuracy, s1->rbexp, s1->linear); 1730dc766f2bSmlelstv dbg_printf(10, "values=%#.2x %#.2x %#.2x %#.2x %s\n", 1731dc766f2bSmlelstv data[0],data[1],data[2],data[3], edata->desc); 1732dc766f2bSmlelstv if (IPMI_INVALID_SENSOR_P(data[1])) { 1733dc766f2bSmlelstv /* Check if sensor is valid */ 1734dc766f2bSmlelstv edata->state = ENVSYS_SINVALID; 1735dc766f2bSmlelstv } else { 1736dc766f2bSmlelstv edata->state = ipmi_sensor_status(sc, psensor, edata, data); 1737dc766f2bSmlelstv } 1738dc766f2bSmlelstv return 0; 1739dc766f2bSmlelstv err: 1740dc766f2bSmlelstv mutex_exit(&sc->sc_cmd_mtx); 1741dc766f2bSmlelstv return -1; 1742dc766f2bSmlelstv } 1743dc766f2bSmlelstv 1744dc766f2bSmlelstv static int 1745dc766f2bSmlelstv ipmi_sensor_type(int type, int ext_type, int entity) 1746dc766f2bSmlelstv { 1747dc766f2bSmlelstv switch (ext_type << 8L | type) { 1748dc766f2bSmlelstv case IPMI_SENSOR_TYPE_TEMP: 1749dc766f2bSmlelstv return ENVSYS_STEMP; 1750dc766f2bSmlelstv 1751dc766f2bSmlelstv case IPMI_SENSOR_TYPE_VOLT: 1752dc766f2bSmlelstv return ENVSYS_SVOLTS_DC; 1753dc766f2bSmlelstv 1754dc766f2bSmlelstv case IPMI_SENSOR_TYPE_FAN: 1755dc766f2bSmlelstv return ENVSYS_SFANRPM; 1756dc766f2bSmlelstv 1757dc766f2bSmlelstv case IPMI_SENSOR_TYPE_PWRSUPPLY: 1758dc766f2bSmlelstv if (entity == IPMI_ENTITY_PWRSUPPLY) 1759dc766f2bSmlelstv return ENVSYS_INDICATOR; 1760dc766f2bSmlelstv break; 1761dc766f2bSmlelstv 1762dc766f2bSmlelstv case IPMI_SENSOR_TYPE_INTRUSION: 1763dc766f2bSmlelstv return ENVSYS_INDICATOR; 1764dc766f2bSmlelstv } 1765dc766f2bSmlelstv 1766dc766f2bSmlelstv return -1; 1767dc766f2bSmlelstv } 1768dc766f2bSmlelstv 1769dc766f2bSmlelstv /* Add Sensor to BSD Sysctl interface */ 1770dc766f2bSmlelstv static int 1771dc766f2bSmlelstv add_sdr_sensor(struct ipmi_softc *sc, uint8_t *psdr) 1772dc766f2bSmlelstv { 1773dc766f2bSmlelstv int rc; 1774dc766f2bSmlelstv struct sdrtype1 *s1 = (struct sdrtype1 *)psdr; 1775dc766f2bSmlelstv struct sdrtype2 *s2 = (struct sdrtype2 *)psdr; 1776dc766f2bSmlelstv char name[64]; 1777dc766f2bSmlelstv 1778dc766f2bSmlelstv switch (s1->sdrhdr.record_type) { 1779dc766f2bSmlelstv case IPMI_SDR_TYPEFULL: 1780dc766f2bSmlelstv ipmi_sensor_name(name, sizeof(name), s1->typelen, s1->name); 1781dc766f2bSmlelstv rc = add_child_sensors(sc, psdr, 1, s1->sensor_num, 1782dc766f2bSmlelstv s1->sensor_type, s1->event_code, 0, s1->entity_id, name); 1783dc766f2bSmlelstv break; 1784dc766f2bSmlelstv 1785dc766f2bSmlelstv case IPMI_SDR_TYPECOMPACT: 1786dc766f2bSmlelstv ipmi_sensor_name(name, sizeof(name), s2->typelen, s2->name); 1787dc766f2bSmlelstv rc = add_child_sensors(sc, psdr, s2->share1 & 0xF, 1788dc766f2bSmlelstv s2->sensor_num, s2->sensor_type, s2->event_code, 1789dc766f2bSmlelstv s2->share2 & 0x7F, s2->entity_id, name); 1790dc766f2bSmlelstv break; 1791dc766f2bSmlelstv 1792dc766f2bSmlelstv default: 1793dc766f2bSmlelstv return 0; 1794dc766f2bSmlelstv } 1795dc766f2bSmlelstv 1796dc766f2bSmlelstv return rc; 1797dc766f2bSmlelstv } 1798dc766f2bSmlelstv 1799dc766f2bSmlelstv static int 1800dc766f2bSmlelstv ipmi_is_dupname(char *name) 1801dc766f2bSmlelstv { 1802dc766f2bSmlelstv struct ipmi_sensor *ipmi_s; 1803dc766f2bSmlelstv 1804dc766f2bSmlelstv SLIST_FOREACH(ipmi_s, &ipmi_sensor_list, i_list) { 1805dc766f2bSmlelstv if (strcmp(ipmi_s->i_envdesc, name) == 0) { 1806dc766f2bSmlelstv return 1; 1807dc766f2bSmlelstv } 1808dc766f2bSmlelstv } 1809dc766f2bSmlelstv return 0; 1810dc766f2bSmlelstv } 1811dc766f2bSmlelstv 1812dc766f2bSmlelstv static int 1813dc766f2bSmlelstv add_child_sensors(struct ipmi_softc *sc, uint8_t *psdr, int count, 1814dc766f2bSmlelstv int sensor_num, int sensor_type, int ext_type, int sensor_base, 1815dc766f2bSmlelstv int entity, const char *name) 1816dc766f2bSmlelstv { 1817dc766f2bSmlelstv int typ, idx, dupcnt, c; 1818dc766f2bSmlelstv char *e; 1819dc766f2bSmlelstv struct ipmi_sensor *psensor; 1820dc766f2bSmlelstv struct sdrtype1 *s1 = (struct sdrtype1 *)psdr; 1821dc766f2bSmlelstv 1822dc766f2bSmlelstv typ = ipmi_sensor_type(sensor_type, ext_type, entity); 1823dc766f2bSmlelstv if (typ == -1) { 1824dc766f2bSmlelstv dbg_printf(5, "Unknown sensor type:%#.2x et:%#.2x sn:%#.2x " 1825dc766f2bSmlelstv "name:%s\n", sensor_type, ext_type, sensor_num, name); 1826dc766f2bSmlelstv return 0; 1827dc766f2bSmlelstv } 1828dc766f2bSmlelstv dupcnt = 0; 1829dc766f2bSmlelstv sc->sc_nsensors += count; 1830dc766f2bSmlelstv for (idx = 0; idx < count; idx++) { 1831dc766f2bSmlelstv psensor = malloc(sizeof(struct ipmi_sensor), M_DEVBUF, 1832dc766f2bSmlelstv M_WAITOK); 1833dc766f2bSmlelstv if (psensor == NULL) 1834dc766f2bSmlelstv break; 1835dc766f2bSmlelstv 1836dc766f2bSmlelstv memset(psensor, 0, sizeof(struct ipmi_sensor)); 1837dc766f2bSmlelstv 1838dc766f2bSmlelstv /* Initialize BSD Sensor info */ 1839dc766f2bSmlelstv psensor->i_sdr = psdr; 1840dc766f2bSmlelstv psensor->i_num = sensor_num + idx; 1841dc766f2bSmlelstv psensor->i_stype = sensor_type; 1842dc766f2bSmlelstv psensor->i_etype = ext_type; 1843dc766f2bSmlelstv psensor->i_envtype = typ; 1844dc766f2bSmlelstv if (count > 1) 1845dc766f2bSmlelstv snprintf(psensor->i_envdesc, 1846dc766f2bSmlelstv sizeof(psensor->i_envdesc), 1847dc766f2bSmlelstv "%s - %d", name, sensor_base + idx); 1848dc766f2bSmlelstv else 1849dc766f2bSmlelstv strlcpy(psensor->i_envdesc, name, 1850dc766f2bSmlelstv sizeof(psensor->i_envdesc)); 1851dc766f2bSmlelstv 1852dc766f2bSmlelstv /* 1853dc766f2bSmlelstv * Check for duplicates. If there are duplicates, 1854dc766f2bSmlelstv * make sure there is space in the name (if not, 1855dc766f2bSmlelstv * truncate to make space) for a count (1-99) to 1856dc766f2bSmlelstv * add to make the name unique. If we run the 1857dc766f2bSmlelstv * counter out, just accept the duplicate (@name99) 1858dc766f2bSmlelstv * for now. 1859dc766f2bSmlelstv */ 1860dc766f2bSmlelstv if (ipmi_is_dupname(psensor->i_envdesc)) { 1861dc766f2bSmlelstv if (strlen(psensor->i_envdesc) >= 1862dc766f2bSmlelstv sizeof(psensor->i_envdesc) - 3) { 1863dc766f2bSmlelstv e = psensor->i_envdesc + 1864dc766f2bSmlelstv sizeof(psensor->i_envdesc) - 3; 1865dc766f2bSmlelstv } else { 1866dc766f2bSmlelstv e = psensor->i_envdesc + 1867dc766f2bSmlelstv strlen(psensor->i_envdesc); 1868dc766f2bSmlelstv } 1869dc766f2bSmlelstv c = psensor->i_envdesc + 1870dc766f2bSmlelstv sizeof(psensor->i_envdesc) - e; 1871dc766f2bSmlelstv do { 1872dc766f2bSmlelstv dupcnt++; 1873dc766f2bSmlelstv snprintf(e, c, "%d", dupcnt); 1874dc766f2bSmlelstv } while (dupcnt < 100 && 1875dc766f2bSmlelstv ipmi_is_dupname(psensor->i_envdesc)); 1876dc766f2bSmlelstv } 1877dc766f2bSmlelstv 1878dc766f2bSmlelstv dbg_printf(5, "%s: %#.4x %#.2x:%d ent:%#.2x:%#.2x %s\n", 1879dc766f2bSmlelstv __func__, 1880dc766f2bSmlelstv s1->sdrhdr.record_id, s1->sensor_type, 1881dc766f2bSmlelstv typ, s1->entity_id, s1->entity_instance, 1882dc766f2bSmlelstv psensor->i_envdesc); 1883dc766f2bSmlelstv SLIST_INSERT_HEAD(&ipmi_sensor_list, psensor, i_list); 1884dc766f2bSmlelstv } 1885dc766f2bSmlelstv 1886dc766f2bSmlelstv return 1; 1887dc766f2bSmlelstv } 1888dc766f2bSmlelstv 1889dc766f2bSmlelstv #if 0 1890dc766f2bSmlelstv /* Interrupt handler */ 1891dc766f2bSmlelstv static int 1892dc766f2bSmlelstv ipmi_intr(void *arg) 1893dc766f2bSmlelstv { 1894dc766f2bSmlelstv struct ipmi_softc *sc = (struct ipmi_softc *)arg; 1895dc766f2bSmlelstv int v; 1896dc766f2bSmlelstv 1897dc766f2bSmlelstv v = bmc_read(sc, _KCS_STATUS_REGISTER); 1898dc766f2bSmlelstv if (v & KCS_OBF) 1899dc766f2bSmlelstv ++ipmi_nintr; 1900dc766f2bSmlelstv 1901dc766f2bSmlelstv return 0; 1902dc766f2bSmlelstv } 1903dc766f2bSmlelstv #endif 1904dc766f2bSmlelstv 1905dc766f2bSmlelstv /* Handle IPMI Timer - reread sensor values */ 1906dc766f2bSmlelstv static void 1907dc766f2bSmlelstv ipmi_refresh_sensors(struct ipmi_softc *sc) 1908dc766f2bSmlelstv { 1909dc766f2bSmlelstv 1910dc766f2bSmlelstv if (SLIST_EMPTY(&ipmi_sensor_list)) 1911dc766f2bSmlelstv return; 1912dc766f2bSmlelstv 1913dc766f2bSmlelstv sc->current_sensor = SLIST_NEXT(sc->current_sensor, i_list); 1914dc766f2bSmlelstv if (sc->current_sensor == NULL) 1915dc766f2bSmlelstv sc->current_sensor = SLIST_FIRST(&ipmi_sensor_list); 1916dc766f2bSmlelstv 1917dc766f2bSmlelstv if (read_sensor(sc, sc->current_sensor)) { 1918dc766f2bSmlelstv dbg_printf(1, "%s: error reading\n", __func__); 1919dc766f2bSmlelstv } 1920dc766f2bSmlelstv } 1921dc766f2bSmlelstv 1922dc766f2bSmlelstv static int 1923dc766f2bSmlelstv ipmi_map_regs(struct ipmi_softc *sc, struct ipmi_attach_args *ia) 1924dc766f2bSmlelstv { 1925dc766f2bSmlelstv int error; 1926dc766f2bSmlelstv 1927dc766f2bSmlelstv sc->sc_if = ipmi_get_if(ia->iaa_if_type); 1928dc766f2bSmlelstv if (sc->sc_if == NULL) 1929dc766f2bSmlelstv return -1; 1930dc766f2bSmlelstv 1931dc766f2bSmlelstv if (ia->iaa_if_iotype == 'i') 1932dc766f2bSmlelstv sc->sc_iot = ia->iaa_iot; 1933dc766f2bSmlelstv else 1934dc766f2bSmlelstv sc->sc_iot = ia->iaa_memt; 1935dc766f2bSmlelstv 1936dc766f2bSmlelstv sc->sc_if_rev = ia->iaa_if_rev; 1937dc766f2bSmlelstv sc->sc_if_iospacing = ia->iaa_if_iospacing; 1938dc766f2bSmlelstv if ((error = bus_space_map(sc->sc_iot, ia->iaa_if_iobase, 1939dc766f2bSmlelstv sc->sc_if->nregs * sc->sc_if_iospacing, 0, &sc->sc_ioh)) != 0) { 1940dc766f2bSmlelstv const char *xname = sc->sc_dev ? device_xname(sc->sc_dev) : 1941dc766f2bSmlelstv "ipmi0"; 1942dc766f2bSmlelstv aprint_error("%s: %s:bus_space_map(..., %" PRIx64 ", %x" 1943dc766f2bSmlelstv ", 0, %p) type %c failed %d\n", 1944e47e31c9Smlelstv xname, __func__, (uint64_t)ia->iaa_if_iobase, 1945dc766f2bSmlelstv sc->sc_if->nregs * sc->sc_if_iospacing, &sc->sc_ioh, 1946dc766f2bSmlelstv ia->iaa_if_iotype, error); 1947dc766f2bSmlelstv return -1; 1948dc766f2bSmlelstv } 1949dc766f2bSmlelstv #if 0 1950dc766f2bSmlelstv if (iaa->if_if_irq != -1) 1951dc766f2bSmlelstv sc->ih = isa_intr_establish(-1, iaa->if_if_irq, 1952dc766f2bSmlelstv iaa->if_irqlvl, IPL_BIO, ipmi_intr, sc, 1953dc766f2bSmlelstv device_xname(sc->sc_dev); 1954dc766f2bSmlelstv #endif 1955dc766f2bSmlelstv return 0; 1956dc766f2bSmlelstv } 1957dc766f2bSmlelstv 1958dc766f2bSmlelstv static void 1959dc766f2bSmlelstv ipmi_unmap_regs(struct ipmi_softc *sc) 1960dc766f2bSmlelstv { 1961dc766f2bSmlelstv bus_space_unmap(sc->sc_iot, sc->sc_ioh, 1962dc766f2bSmlelstv sc->sc_if->nregs * sc->sc_if_iospacing); 1963dc766f2bSmlelstv } 1964dc766f2bSmlelstv 1965dc766f2bSmlelstv static int 1966dc766f2bSmlelstv ipmi_match(device_t parent, cfdata_t cf, void *aux) 1967dc766f2bSmlelstv { 1968dc766f2bSmlelstv struct ipmi_softc sc; 1969dc766f2bSmlelstv struct ipmi_attach_args *ia = aux; 1970dc766f2bSmlelstv int rv = 0; 1971dc766f2bSmlelstv 1972dc766f2bSmlelstv memset(&sc, 0, sizeof(sc)); 1973dc766f2bSmlelstv 1974dc766f2bSmlelstv /* Map registers */ 1975dc766f2bSmlelstv if (ipmi_map_regs(&sc, ia) != 0) 1976dc766f2bSmlelstv return 0; 1977dc766f2bSmlelstv 1978dc766f2bSmlelstv sc.sc_if->probe(&sc); 1979dc766f2bSmlelstv 1980dc766f2bSmlelstv mutex_init(&sc.sc_cmd_mtx, MUTEX_DEFAULT, IPL_SOFTCLOCK); 1981dc766f2bSmlelstv 1982f95aa101Smlelstv if (ipmi_get_device_id(&sc, NULL) == 0) 1983f95aa101Smlelstv rv = 1; 1984f95aa101Smlelstv 1985dc766f2bSmlelstv mutex_destroy(&sc.sc_cmd_mtx); 1986dc766f2bSmlelstv ipmi_unmap_regs(&sc); 1987dc766f2bSmlelstv 1988dc766f2bSmlelstv return rv; 1989dc766f2bSmlelstv } 1990dc766f2bSmlelstv 1991dc766f2bSmlelstv static void 1992dc766f2bSmlelstv ipmi_thread(void *cookie) 1993dc766f2bSmlelstv { 1994dc766f2bSmlelstv device_t self = cookie; 1995dc766f2bSmlelstv struct ipmi_softc *sc = device_private(self); 1996dc766f2bSmlelstv struct ipmi_attach_args *ia = &sc->sc_ia; 1997dc766f2bSmlelstv uint16_t rec; 1998dc766f2bSmlelstv struct ipmi_sensor *ipmi_s; 1999f95aa101Smlelstv struct ipmi_device_id id; 2000dc766f2bSmlelstv int i; 2001dc766f2bSmlelstv 2002dc766f2bSmlelstv sc->sc_thread_running = true; 2003dc766f2bSmlelstv 2004dc766f2bSmlelstv /* setup ticker */ 2005dc766f2bSmlelstv sc->sc_max_retries = hz * 90; /* 90 seconds max */ 2006dc766f2bSmlelstv 2007dc766f2bSmlelstv /* Map registers */ 2008dc766f2bSmlelstv ipmi_map_regs(sc, ia); 2009dc766f2bSmlelstv 2010b851421cSriastradh /* Setup Watchdog timer */ 2011b851421cSriastradh sc->sc_wdog.smw_name = device_xname(sc->sc_dev); 2012b851421cSriastradh sc->sc_wdog.smw_cookie = sc; 2013b851421cSriastradh sc->sc_wdog.smw_setmode = ipmi_watchdog_setmode; 2014b851421cSriastradh sc->sc_wdog.smw_tickle = ipmi_watchdog_tickle; 2015b851421cSriastradh sysmon_wdog_register(&sc->sc_wdog); 2016b851421cSriastradh 2017b851421cSriastradh /* Set up a power handler so we can possibly sleep */ 2018b851421cSriastradh if (!pmf_device_register(self, ipmi_suspend, NULL)) 2019b851421cSriastradh aprint_error_dev(self, "couldn't establish a power handler\n"); 2020b851421cSriastradh 2021b851421cSriastradh /* 2022b851421cSriastradh * Allow boot to proceed -- we'll do the rest asynchronously 2023b851421cSriastradh * since it requires talking to the device. 2024b851421cSriastradh */ 2025b851421cSriastradh config_pending_decr(self); 2026b851421cSriastradh 2027f95aa101Smlelstv memset(&id, 0, sizeof(id)); 2028f95aa101Smlelstv if (ipmi_get_device_id(sc, &id)) 2029f95aa101Smlelstv aprint_error_dev(self, "Failed to re-query device ID\n"); 2030f95aa101Smlelstv 2031dc766f2bSmlelstv /* Scan SDRs, add sensors to list */ 2032dc766f2bSmlelstv for (rec = 0; rec != 0xFFFF;) 2033dc766f2bSmlelstv if (get_sdr(sc, rec, &rec)) 2034dc766f2bSmlelstv break; 2035dc766f2bSmlelstv 2036dc766f2bSmlelstv /* allocate and fill sensor arrays */ 203776313cf4Sriastradh sc->sc_sensor = malloc(sizeof(sc->sc_sensor[0]) * sc->sc_nsensors, 2038dc766f2bSmlelstv M_DEVBUF, M_WAITOK | M_ZERO); 2039dc766f2bSmlelstv 2040dc766f2bSmlelstv sc->sc_envsys = sysmon_envsys_create(); 2041dc766f2bSmlelstv sc->sc_envsys->sme_cookie = sc; 2042dc766f2bSmlelstv sc->sc_envsys->sme_get_limits = ipmi_get_limits; 2043dc766f2bSmlelstv sc->sc_envsys->sme_set_limits = ipmi_set_limits; 2044dc766f2bSmlelstv 2045dc766f2bSmlelstv i = 0; 2046dc766f2bSmlelstv SLIST_FOREACH(ipmi_s, &ipmi_sensor_list, i_list) { 2047dc766f2bSmlelstv ipmi_s->i_props = 0; 2048dc766f2bSmlelstv ipmi_s->i_envnum = -1; 2049dc766f2bSmlelstv sc->sc_sensor[i].units = ipmi_s->i_envtype; 2050dc766f2bSmlelstv sc->sc_sensor[i].state = ENVSYS_SINVALID; 2051dc766f2bSmlelstv sc->sc_sensor[i].flags |= ENVSYS_FHAS_ENTROPY; 2052dc766f2bSmlelstv /* 2053dc766f2bSmlelstv * Monitor threshold limits in the sensors. 2054dc766f2bSmlelstv */ 2055dc766f2bSmlelstv switch (sc->sc_sensor[i].units) { 2056dc766f2bSmlelstv case ENVSYS_STEMP: 2057dc766f2bSmlelstv case ENVSYS_SVOLTS_DC: 2058dc766f2bSmlelstv case ENVSYS_SFANRPM: 2059dc766f2bSmlelstv sc->sc_sensor[i].flags |= ENVSYS_FMONLIMITS; 2060dc766f2bSmlelstv break; 2061dc766f2bSmlelstv default: 2062dc766f2bSmlelstv sc->sc_sensor[i].flags |= ENVSYS_FMONCRITICAL; 2063dc766f2bSmlelstv } 2064dc766f2bSmlelstv (void)strlcpy(sc->sc_sensor[i].desc, ipmi_s->i_envdesc, 2065dc766f2bSmlelstv sizeof(sc->sc_sensor[i].desc)); 2066dc766f2bSmlelstv ++i; 2067dc766f2bSmlelstv 2068dc766f2bSmlelstv if (sysmon_envsys_sensor_attach(sc->sc_envsys, 2069dc766f2bSmlelstv &sc->sc_sensor[i-1])) 2070dc766f2bSmlelstv continue; 2071dc766f2bSmlelstv 2072dc766f2bSmlelstv /* get reference number from envsys */ 2073dc766f2bSmlelstv ipmi_s->i_envnum = sc->sc_sensor[i-1].sensor; 2074dc766f2bSmlelstv } 2075dc766f2bSmlelstv 2076dc766f2bSmlelstv sc->sc_envsys->sme_name = device_xname(sc->sc_dev); 2077dc766f2bSmlelstv sc->sc_envsys->sme_flags = SME_DISABLE_REFRESH; 2078dc766f2bSmlelstv 2079dc766f2bSmlelstv if (sysmon_envsys_register(sc->sc_envsys)) { 2080dc766f2bSmlelstv aprint_error_dev(self, "unable to register with sysmon\n"); 2081dc766f2bSmlelstv sysmon_envsys_destroy(sc->sc_envsys); 2082f219451cSmlelstv sc->sc_envsys = NULL; 2083dc766f2bSmlelstv } 2084dc766f2bSmlelstv 2085dc766f2bSmlelstv /* initialize sensor list for thread */ 2086dc766f2bSmlelstv if (!SLIST_EMPTY(&ipmi_sensor_list)) 2087dc766f2bSmlelstv sc->current_sensor = SLIST_FIRST(&ipmi_sensor_list); 2088dc766f2bSmlelstv 2089dc766f2bSmlelstv aprint_verbose_dev(self, "version %d.%d interface %s %sbase " 2090dc766f2bSmlelstv "0x%" PRIx64 "/%#x spacing %d\n", 2091dc766f2bSmlelstv ia->iaa_if_rev >> 4, ia->iaa_if_rev & 0xF, sc->sc_if->name, 2092e47e31c9Smlelstv ia->iaa_if_iotype == 'i' ? "io" : "mem", 2093e47e31c9Smlelstv (uint64_t)ia->iaa_if_iobase, 2094dc766f2bSmlelstv ia->iaa_if_iospacing * sc->sc_if->nregs, ia->iaa_if_iospacing); 2095dc766f2bSmlelstv if (ia->iaa_if_irq != -1) 2096dc766f2bSmlelstv aprint_verbose_dev(self, " irq %d\n", ia->iaa_if_irq); 2097dc766f2bSmlelstv 2098f95aa101Smlelstv if (id.deviceid != 0) { 2099f95aa101Smlelstv aprint_normal_dev(self, "ID %u.%u IPMI %x.%x%s%s\n", 2100f95aa101Smlelstv id.deviceid, (id.revision & 0xf), 2101f95aa101Smlelstv (id.version & 0xf), (id.version >> 4) & 0xf, 2102f95aa101Smlelstv (id.fwrev1 & 0x80) ? " Initializing" : " Available", 2103f95aa101Smlelstv (id.revision & 0x80) ? " +SDRs" : ""); 2104f95aa101Smlelstv if (id.additional != 0) 2105f95aa101Smlelstv aprint_verbose_dev(self, "Additional%s%s%s%s%s%s%s%s\n", 2106f95aa101Smlelstv (id.additional & 0x80) ? " Chassis" : "", 2107f95aa101Smlelstv (id.additional & 0x40) ? " Bridge" : "", 2108f95aa101Smlelstv (id.additional & 0x20) ? " IPMBGen" : "", 2109f95aa101Smlelstv (id.additional & 0x10) ? " IPMBRcv" : "", 2110f95aa101Smlelstv (id.additional & 0x08) ? " FRU" : "", 2111f95aa101Smlelstv (id.additional & 0x04) ? " SEL" : "", 2112f95aa101Smlelstv (id.additional & 0x02) ? " SDR" : "", 2113f95aa101Smlelstv (id.additional & 0x01) ? " Sensor" : ""); 2114f95aa101Smlelstv aprint_verbose_dev(self, "Manufacturer %05x Product %04x\n", 2115f95aa101Smlelstv (id.manufacturer[2] & 0xf) << 16 2116f95aa101Smlelstv | id.manufacturer[1] << 8 2117f95aa101Smlelstv | id.manufacturer[0], 2118f95aa101Smlelstv id.product[1] << 8 2119f95aa101Smlelstv | id.manufacturer[0]); 2120f95aa101Smlelstv aprint_verbose_dev(self, "Firmware %u.%x\n", 2121f95aa101Smlelstv (id.fwrev1 & 0x7f), id.fwrev2); 2122f95aa101Smlelstv } 2123f95aa101Smlelstv 2124dc766f2bSmlelstv /* setup flag to exclude iic */ 2125dc766f2bSmlelstv ipmi_enabled = 1; 2126dc766f2bSmlelstv 2127dc766f2bSmlelstv mutex_enter(&sc->sc_poll_mtx); 2128b851421cSriastradh sc->sc_thread_ready = true; 2129b851421cSriastradh cv_broadcast(&sc->sc_mode_cv); 2130dc766f2bSmlelstv while (sc->sc_thread_running) { 2131b1f23fc9Smlelstv while (sc->sc_mode == IPMI_MODE_COMMAND) 2132b1f23fc9Smlelstv cv_wait(&sc->sc_mode_cv, &sc->sc_poll_mtx); 2133b1f23fc9Smlelstv sc->sc_mode = IPMI_MODE_ENVSYS; 2134b1f23fc9Smlelstv 2135dc766f2bSmlelstv if (sc->sc_tickle_due) { 2136dc766f2bSmlelstv ipmi_dotickle(sc); 2137dc766f2bSmlelstv sc->sc_tickle_due = false; 2138dc766f2bSmlelstv } 2139b1f23fc9Smlelstv ipmi_refresh_sensors(sc); 2140b1f23fc9Smlelstv 2141b1f23fc9Smlelstv sc->sc_mode = IPMI_MODE_IDLE; 2142b1f23fc9Smlelstv cv_broadcast(&sc->sc_mode_cv); 2143b1f23fc9Smlelstv cv_timedwait(&sc->sc_poll_cv, &sc->sc_poll_mtx, 2144b1f23fc9Smlelstv SENSOR_REFRESH_RATE); 2145dc766f2bSmlelstv } 2146dc766f2bSmlelstv mutex_exit(&sc->sc_poll_mtx); 2147dc766f2bSmlelstv kthread_exit(0); 2148dc766f2bSmlelstv } 2149dc766f2bSmlelstv 2150dc766f2bSmlelstv static void 2151dc766f2bSmlelstv ipmi_attach(device_t parent, device_t self, void *aux) 2152dc766f2bSmlelstv { 2153dc766f2bSmlelstv struct ipmi_softc *sc = device_private(self); 2154dc766f2bSmlelstv 2155dc766f2bSmlelstv sc->sc_ia = *(struct ipmi_attach_args *)aux; 2156dc766f2bSmlelstv sc->sc_dev = self; 2157dc766f2bSmlelstv aprint_naive("\n"); 2158dc766f2bSmlelstv aprint_normal("\n"); 2159dc766f2bSmlelstv 2160dc766f2bSmlelstv /* lock around read_sensor so that no one messes with the bmc regs */ 2161dc766f2bSmlelstv mutex_init(&sc->sc_cmd_mtx, MUTEX_DEFAULT, IPL_SOFTCLOCK); 2162dc766f2bSmlelstv 2163dc766f2bSmlelstv mutex_init(&sc->sc_poll_mtx, MUTEX_DEFAULT, IPL_SOFTCLOCK); 2164dc766f2bSmlelstv cv_init(&sc->sc_poll_cv, "ipmipoll"); 2165b1f23fc9Smlelstv cv_init(&sc->sc_mode_cv, "ipmimode"); 2166dc766f2bSmlelstv 216776313cf4Sriastradh if (kthread_create(PRI_NONE, KTHREAD_MUSTJOIN, NULL, ipmi_thread, self, 2168dc766f2bSmlelstv &sc->sc_kthread, "%s", device_xname(self)) != 0) { 2169dc766f2bSmlelstv aprint_error_dev(self, "unable to create thread, disabled\n"); 2170dc766f2bSmlelstv } else 21710052d108Sriastradh config_pending_incr(self); 2172dc766f2bSmlelstv } 2173dc766f2bSmlelstv 2174dc766f2bSmlelstv static int 2175dc766f2bSmlelstv ipmi_detach(device_t self, int flags) 2176dc766f2bSmlelstv { 2177dc766f2bSmlelstv struct ipmi_sensor *i; 2178dc766f2bSmlelstv int rc; 2179dc766f2bSmlelstv struct ipmi_softc *sc = device_private(self); 2180dc766f2bSmlelstv 2181dc766f2bSmlelstv mutex_enter(&sc->sc_poll_mtx); 2182dc766f2bSmlelstv sc->sc_thread_running = false; 2183dc766f2bSmlelstv cv_signal(&sc->sc_poll_cv); 2184dc766f2bSmlelstv mutex_exit(&sc->sc_poll_mtx); 218576313cf4Sriastradh if (sc->sc_kthread) 218676313cf4Sriastradh (void)kthread_join(sc->sc_kthread); 2187dc766f2bSmlelstv 2188dc766f2bSmlelstv if ((rc = sysmon_wdog_unregister(&sc->sc_wdog)) != 0) { 2189dc766f2bSmlelstv if (rc == ERESTART) 2190dc766f2bSmlelstv rc = EINTR; 2191dc766f2bSmlelstv return rc; 2192dc766f2bSmlelstv } 2193dc766f2bSmlelstv 2194dc766f2bSmlelstv /* cancel any pending countdown */ 2195dc766f2bSmlelstv sc->sc_wdog.smw_mode &= ~WDOG_MODE_MASK; 2196dc766f2bSmlelstv sc->sc_wdog.smw_mode |= WDOG_MODE_DISARMED; 2197dc766f2bSmlelstv sc->sc_wdog.smw_period = WDOG_PERIOD_DEFAULT; 2198dc766f2bSmlelstv 2199dc766f2bSmlelstv if ((rc = ipmi_watchdog_setmode(&sc->sc_wdog)) != 0) 2200dc766f2bSmlelstv return rc; 2201dc766f2bSmlelstv 2202dc766f2bSmlelstv ipmi_enabled = 0; 2203dc766f2bSmlelstv 2204dc766f2bSmlelstv if (sc->sc_envsys != NULL) { 2205dc766f2bSmlelstv /* _unregister also destroys */ 2206dc766f2bSmlelstv sysmon_envsys_unregister(sc->sc_envsys); 2207dc766f2bSmlelstv sc->sc_envsys = NULL; 2208dc766f2bSmlelstv } 2209dc766f2bSmlelstv 2210dc766f2bSmlelstv while ((i = SLIST_FIRST(&ipmi_sensor_list)) != NULL) { 2211dc766f2bSmlelstv SLIST_REMOVE_HEAD(&ipmi_sensor_list, i_list); 2212dc766f2bSmlelstv free(i, M_DEVBUF); 2213dc766f2bSmlelstv } 2214dc766f2bSmlelstv 2215dc766f2bSmlelstv if (sc->sc_sensor != NULL) { 2216dc766f2bSmlelstv free(sc->sc_sensor, M_DEVBUF); 2217dc766f2bSmlelstv sc->sc_sensor = NULL; 2218dc766f2bSmlelstv } 2219dc766f2bSmlelstv 2220dc766f2bSmlelstv ipmi_unmap_regs(sc); 2221dc766f2bSmlelstv 2222b1f23fc9Smlelstv cv_destroy(&sc->sc_mode_cv); 2223dc766f2bSmlelstv cv_destroy(&sc->sc_poll_cv); 2224dc766f2bSmlelstv mutex_destroy(&sc->sc_poll_mtx); 2225dc766f2bSmlelstv mutex_destroy(&sc->sc_cmd_mtx); 2226dc766f2bSmlelstv 2227dc766f2bSmlelstv return 0; 2228dc766f2bSmlelstv } 2229dc766f2bSmlelstv 2230dc766f2bSmlelstv static int 2231f95aa101Smlelstv ipmi_get_device_id(struct ipmi_softc *sc, struct ipmi_device_id *res) 2232f95aa101Smlelstv { 2233f95aa101Smlelstv uint8_t buf[32]; 2234f95aa101Smlelstv int len; 2235f95aa101Smlelstv int rc; 2236f95aa101Smlelstv 2237f95aa101Smlelstv mutex_enter(&sc->sc_cmd_mtx); 2238f95aa101Smlelstv /* Identify BMC device early to detect lying bios */ 2239f95aa101Smlelstv rc = ipmi_sendcmd(sc, BMC_SA, 0, APP_NETFN, APP_GET_DEVICE_ID, 0, NULL); 2240f95aa101Smlelstv if (rc) { 2241f95aa101Smlelstv dbg_printf(1, ": unable to send get device id " 2242f95aa101Smlelstv "command\n"); 2243f95aa101Smlelstv goto done; 2244f95aa101Smlelstv } 2245f95aa101Smlelstv rc = ipmi_recvcmd(sc, sizeof(buf), &len, buf); 2246f95aa101Smlelstv if (rc) { 2247f95aa101Smlelstv dbg_printf(1, ": unable to retrieve device id\n"); 2248f95aa101Smlelstv } 2249f95aa101Smlelstv done: 2250f95aa101Smlelstv mutex_exit(&sc->sc_cmd_mtx); 2251f95aa101Smlelstv 2252f95aa101Smlelstv if (rc == 0 && res != NULL) 2253f95aa101Smlelstv memcpy(res, buf, MIN(sizeof(*res), len)); 2254f95aa101Smlelstv 2255f95aa101Smlelstv return rc; 2256f95aa101Smlelstv } 2257f95aa101Smlelstv 2258f95aa101Smlelstv static int 2259dc766f2bSmlelstv ipmi_watchdog_setmode(struct sysmon_wdog *smwdog) 2260dc766f2bSmlelstv { 2261dc766f2bSmlelstv struct ipmi_softc *sc = smwdog->smw_cookie; 2262dc766f2bSmlelstv struct ipmi_get_watchdog gwdog; 2263dc766f2bSmlelstv struct ipmi_set_watchdog swdog; 2264dc766f2bSmlelstv int rc, len; 2265dc766f2bSmlelstv 2266dc766f2bSmlelstv if (smwdog->smw_period < 10) 2267dc766f2bSmlelstv return EINVAL; 2268dc766f2bSmlelstv if (smwdog->smw_period == WDOG_PERIOD_DEFAULT) 2269dc766f2bSmlelstv sc->sc_wdog.smw_period = 10; 2270dc766f2bSmlelstv else 2271dc766f2bSmlelstv sc->sc_wdog.smw_period = smwdog->smw_period; 2272dc766f2bSmlelstv 2273b851421cSriastradh /* Wait until the device is initialized */ 2274b851421cSriastradh rc = 0; 2275b851421cSriastradh mutex_enter(&sc->sc_poll_mtx); 2276b851421cSriastradh while (sc->sc_thread_ready) 2277b851421cSriastradh rc = cv_wait_sig(&sc->sc_mode_cv, &sc->sc_poll_mtx); 2278b851421cSriastradh mutex_exit(&sc->sc_poll_mtx); 2279b851421cSriastradh if (rc) 2280b851421cSriastradh return rc; 2281b851421cSriastradh 2282dc766f2bSmlelstv mutex_enter(&sc->sc_cmd_mtx); 2283dc766f2bSmlelstv /* see if we can properly task to the watchdog */ 2284dc766f2bSmlelstv rc = ipmi_sendcmd(sc, BMC_SA, BMC_LUN, APP_NETFN, 2285dc766f2bSmlelstv APP_GET_WATCHDOG_TIMER, 0, NULL); 2286dc766f2bSmlelstv rc = ipmi_recvcmd(sc, sizeof(gwdog), &len, &gwdog); 2287dc766f2bSmlelstv mutex_exit(&sc->sc_cmd_mtx); 2288dc766f2bSmlelstv if (rc) { 2289dc766f2bSmlelstv aprint_error_dev(sc->sc_dev, 2290dc766f2bSmlelstv "APP_GET_WATCHDOG_TIMER returned %#x\n", rc); 2291dc766f2bSmlelstv return EIO; 2292dc766f2bSmlelstv } 2293dc766f2bSmlelstv 2294dc766f2bSmlelstv memset(&swdog, 0, sizeof(swdog)); 2295dc766f2bSmlelstv /* Period is 10ths/sec */ 2296dc766f2bSmlelstv swdog.wdog_timeout = htole16(sc->sc_wdog.smw_period * 10); 2297dc766f2bSmlelstv if ((smwdog->smw_mode & WDOG_MODE_MASK) == WDOG_MODE_DISARMED) 2298dc766f2bSmlelstv swdog.wdog_action = IPMI_WDOG_ACT_DISABLED; 2299dc766f2bSmlelstv else 2300dc766f2bSmlelstv swdog.wdog_action = IPMI_WDOG_ACT_RESET; 2301dc766f2bSmlelstv swdog.wdog_use = IPMI_WDOG_USE_USE_OS; 2302dc766f2bSmlelstv 2303dc766f2bSmlelstv mutex_enter(&sc->sc_cmd_mtx); 2304dc766f2bSmlelstv if ((rc = ipmi_sendcmd(sc, BMC_SA, BMC_LUN, APP_NETFN, 2305dc766f2bSmlelstv APP_SET_WATCHDOG_TIMER, sizeof(swdog), &swdog)) == 0) 2306dc766f2bSmlelstv rc = ipmi_recvcmd(sc, 0, &len, NULL); 2307dc766f2bSmlelstv mutex_exit(&sc->sc_cmd_mtx); 2308dc766f2bSmlelstv if (rc) { 2309dc766f2bSmlelstv aprint_error_dev(sc->sc_dev, 2310dc766f2bSmlelstv "APP_SET_WATCHDOG_TIMER returned %#x\n", rc); 2311dc766f2bSmlelstv return EIO; 2312dc766f2bSmlelstv } 2313dc766f2bSmlelstv 2314dc766f2bSmlelstv return 0; 2315dc766f2bSmlelstv } 2316dc766f2bSmlelstv 2317dc766f2bSmlelstv static int 2318dc766f2bSmlelstv ipmi_watchdog_tickle(struct sysmon_wdog *smwdog) 2319dc766f2bSmlelstv { 2320dc766f2bSmlelstv struct ipmi_softc *sc = smwdog->smw_cookie; 2321dc766f2bSmlelstv 2322dc766f2bSmlelstv mutex_enter(&sc->sc_poll_mtx); 2323dc766f2bSmlelstv sc->sc_tickle_due = true; 2324dc766f2bSmlelstv cv_signal(&sc->sc_poll_cv); 2325dc766f2bSmlelstv mutex_exit(&sc->sc_poll_mtx); 2326dc766f2bSmlelstv return 0; 2327dc766f2bSmlelstv } 2328dc766f2bSmlelstv 2329dc766f2bSmlelstv static void 2330dc766f2bSmlelstv ipmi_dotickle(struct ipmi_softc *sc) 2331dc766f2bSmlelstv { 2332dc766f2bSmlelstv int rc, len; 2333dc766f2bSmlelstv 2334dc766f2bSmlelstv mutex_enter(&sc->sc_cmd_mtx); 2335dc766f2bSmlelstv /* tickle the watchdog */ 2336dc766f2bSmlelstv if ((rc = ipmi_sendcmd(sc, BMC_SA, BMC_LUN, APP_NETFN, 2337dc766f2bSmlelstv APP_RESET_WATCHDOG, 0, NULL)) == 0) 2338dc766f2bSmlelstv rc = ipmi_recvcmd(sc, 0, &len, NULL); 2339dc766f2bSmlelstv mutex_exit(&sc->sc_cmd_mtx); 2340dc766f2bSmlelstv if (rc != 0) { 2341dc766f2bSmlelstv aprint_error_dev(sc->sc_dev, "watchdog tickle returned %#x\n", 2342dc766f2bSmlelstv rc); 2343dc766f2bSmlelstv } 2344dc766f2bSmlelstv } 2345dc766f2bSmlelstv 2346dc766f2bSmlelstv static bool 2347dc766f2bSmlelstv ipmi_suspend(device_t dev, const pmf_qual_t *qual) 2348dc766f2bSmlelstv { 2349dc766f2bSmlelstv struct ipmi_softc *sc = device_private(dev); 2350dc766f2bSmlelstv 2351dc766f2bSmlelstv /* Don't allow suspend if watchdog is armed */ 2352dc766f2bSmlelstv if ((sc->sc_wdog.smw_mode & WDOG_MODE_MASK) != WDOG_MODE_DISARMED) 2353dc766f2bSmlelstv return false; 2354dc766f2bSmlelstv return true; 2355dc766f2bSmlelstv } 2356b1f23fc9Smlelstv 2357b1f23fc9Smlelstv static int 2358b1f23fc9Smlelstv ipmi_open(dev_t dev, int flag, int fmt, lwp_t *l) 2359b1f23fc9Smlelstv { 2360f219451cSmlelstv struct ipmi_softc *sc; 2361f219451cSmlelstv int unit; 2362f219451cSmlelstv 2363f219451cSmlelstv unit = IPMIUNIT(dev); 2364f219451cSmlelstv if ((sc = device_lookup_private(&ipmi_cd, unit)) == NULL) 2365f219451cSmlelstv return (ENXIO); 2366f219451cSmlelstv 2367b1f23fc9Smlelstv return 0; 2368b1f23fc9Smlelstv } 2369b1f23fc9Smlelstv 2370b1f23fc9Smlelstv static int 2371b1f23fc9Smlelstv ipmi_close(dev_t dev, int flag, int fmt, lwp_t *l) 2372b1f23fc9Smlelstv { 2373b1f23fc9Smlelstv struct ipmi_softc *sc; 2374b1f23fc9Smlelstv int unit; 2375b1f23fc9Smlelstv 2376b1f23fc9Smlelstv unit = IPMIUNIT(dev); 2377b1f23fc9Smlelstv if ((sc = device_lookup_private(&ipmi_cd, unit)) == NULL) 2378b1f23fc9Smlelstv return (ENXIO); 2379b1f23fc9Smlelstv 2380b1f23fc9Smlelstv mutex_enter(&sc->sc_poll_mtx); 2381b1f23fc9Smlelstv if (sc->sc_mode == IPMI_MODE_COMMAND) { 2382b1f23fc9Smlelstv sc->sc_mode = IPMI_MODE_IDLE; 2383b1f23fc9Smlelstv cv_broadcast(&sc->sc_mode_cv); 2384b1f23fc9Smlelstv } 2385b1f23fc9Smlelstv mutex_exit(&sc->sc_poll_mtx); 2386b1f23fc9Smlelstv return 0; 2387b1f23fc9Smlelstv } 2388b1f23fc9Smlelstv 2389b1f23fc9Smlelstv static int 2390b1f23fc9Smlelstv ipmi_ioctl(dev_t dev, u_long cmd, void *data, int flag, lwp_t *l) 2391b1f23fc9Smlelstv { 2392b1f23fc9Smlelstv struct ipmi_softc *sc; 2393b1f23fc9Smlelstv int unit, error = 0, len; 2394b1f23fc9Smlelstv struct ipmi_req *req; 2395b1f23fc9Smlelstv struct ipmi_recv *recv; 2396b1f23fc9Smlelstv struct ipmi_addr addr; 2397b1f23fc9Smlelstv unsigned char ccode, *buf = NULL; 2398b1f23fc9Smlelstv 2399b1f23fc9Smlelstv unit = IPMIUNIT(dev); 2400b1f23fc9Smlelstv if ((sc = device_lookup_private(&ipmi_cd, unit)) == NULL) 2401b1f23fc9Smlelstv return (ENXIO); 2402b1f23fc9Smlelstv 2403b1f23fc9Smlelstv switch (cmd) { 2404b1f23fc9Smlelstv case IPMICTL_SEND_COMMAND: 2405b1f23fc9Smlelstv mutex_enter(&sc->sc_poll_mtx); 2406b1f23fc9Smlelstv while (sc->sc_mode == IPMI_MODE_ENVSYS) { 2407b1f23fc9Smlelstv error = cv_wait_sig(&sc->sc_mode_cv, &sc->sc_poll_mtx); 2408b1f23fc9Smlelstv if (error == EINTR) { 2409b1f23fc9Smlelstv mutex_exit(&sc->sc_poll_mtx); 2410b1f23fc9Smlelstv return error; 2411b1f23fc9Smlelstv } 2412b1f23fc9Smlelstv } 2413b1f23fc9Smlelstv sc->sc_mode = IPMI_MODE_COMMAND; 2414b1f23fc9Smlelstv mutex_exit(&sc->sc_poll_mtx); 2415b1f23fc9Smlelstv break; 2416b1f23fc9Smlelstv } 2417b1f23fc9Smlelstv 2418b1f23fc9Smlelstv mutex_enter(&sc->sc_cmd_mtx); 2419b1f23fc9Smlelstv 2420b1f23fc9Smlelstv switch (cmd) { 2421b1f23fc9Smlelstv case IPMICTL_SEND_COMMAND: 2422b1f23fc9Smlelstv req = data; 2423b1f23fc9Smlelstv buf = malloc(IPMI_MAX_RX, M_DEVBUF, M_WAITOK); 2424b1f23fc9Smlelstv 2425b1f23fc9Smlelstv len = req->msg.data_len; 2426b1f23fc9Smlelstv if (len < 0 || len > IPMI_MAX_RX) { 2427b1f23fc9Smlelstv error = EINVAL; 2428b1f23fc9Smlelstv break; 2429b1f23fc9Smlelstv } 2430b1f23fc9Smlelstv 2431b1f23fc9Smlelstv /* clear pending result */ 2432b1f23fc9Smlelstv if (sc->sc_sent) 2433b1f23fc9Smlelstv (void)ipmi_recvcmd(sc, IPMI_MAX_RX, &len, buf); 2434b1f23fc9Smlelstv 2435b1f23fc9Smlelstv /* XXX */ 2436b1f23fc9Smlelstv error = copyin(req->addr, &addr, sizeof(addr)); 2437b1f23fc9Smlelstv if (error) 2438b1f23fc9Smlelstv break; 2439b1f23fc9Smlelstv 2440b1f23fc9Smlelstv error = copyin(req->msg.data, buf, len); 2441b1f23fc9Smlelstv if (error) 2442b1f23fc9Smlelstv break; 2443b1f23fc9Smlelstv 2444b1f23fc9Smlelstv /* save for receive */ 2445b1f23fc9Smlelstv sc->sc_msgid = req->msgid; 2446b1f23fc9Smlelstv sc->sc_netfn = req->msg.netfn; 2447b1f23fc9Smlelstv sc->sc_cmd = req->msg.cmd; 2448b1f23fc9Smlelstv 2449b1f23fc9Smlelstv if (ipmi_sendcmd(sc, BMC_SA, 0, req->msg.netfn, 2450b1f23fc9Smlelstv req->msg.cmd, len, buf)) { 2451b1f23fc9Smlelstv error = EIO; 2452b1f23fc9Smlelstv break; 2453b1f23fc9Smlelstv } 2454b1f23fc9Smlelstv sc->sc_sent = true; 2455b1f23fc9Smlelstv break; 2456b1f23fc9Smlelstv case IPMICTL_RECEIVE_MSG_TRUNC: 2457b1f23fc9Smlelstv case IPMICTL_RECEIVE_MSG: 2458b1f23fc9Smlelstv recv = data; 2459b1f23fc9Smlelstv buf = malloc(IPMI_MAX_RX, M_DEVBUF, M_WAITOK); 2460b1f23fc9Smlelstv 2461b1f23fc9Smlelstv if (recv->msg.data_len < 1) { 2462b1f23fc9Smlelstv error = EINVAL; 2463b1f23fc9Smlelstv break; 2464b1f23fc9Smlelstv } 2465b1f23fc9Smlelstv 2466b1f23fc9Smlelstv /* XXX */ 2467b1f23fc9Smlelstv error = copyin(recv->addr, &addr, sizeof(addr)); 2468b1f23fc9Smlelstv if (error) 2469b1f23fc9Smlelstv break; 2470b1f23fc9Smlelstv 2471b1f23fc9Smlelstv 2472b1f23fc9Smlelstv if (!sc->sc_sent) { 2473b1f23fc9Smlelstv error = EIO; 2474b1f23fc9Smlelstv break; 2475b1f23fc9Smlelstv } 2476b1f23fc9Smlelstv 2477b1f23fc9Smlelstv len = 0; 2478b1f23fc9Smlelstv error = ipmi_recvcmd(sc, IPMI_MAX_RX, &len, buf); 2479b1f23fc9Smlelstv if (error < 0) { 2480b1f23fc9Smlelstv error = EIO; 2481b1f23fc9Smlelstv break; 2482b1f23fc9Smlelstv } 2483b1f23fc9Smlelstv ccode = (unsigned char)error; 2484b1f23fc9Smlelstv sc->sc_sent = false; 2485b1f23fc9Smlelstv 2486b1f23fc9Smlelstv if (len > recv->msg.data_len - 1) { 2487b1f23fc9Smlelstv if (cmd == IPMICTL_RECEIVE_MSG) { 2488b1f23fc9Smlelstv error = EMSGSIZE; 2489b1f23fc9Smlelstv break; 2490b1f23fc9Smlelstv } 2491b1f23fc9Smlelstv len = recv->msg.data_len - 1; 2492b1f23fc9Smlelstv } 2493b1f23fc9Smlelstv 2494b1f23fc9Smlelstv addr.channel = IPMI_BMC_CHANNEL; 2495b1f23fc9Smlelstv 2496b1f23fc9Smlelstv recv->recv_type = IPMI_RESPONSE_RECV_TYPE; 2497b1f23fc9Smlelstv recv->msgid = sc->sc_msgid; 2498b1f23fc9Smlelstv recv->msg.netfn = sc->sc_netfn; 2499b1f23fc9Smlelstv recv->msg.cmd = sc->sc_cmd; 2500b1f23fc9Smlelstv recv->msg.data_len = len+1; 2501b1f23fc9Smlelstv 2502b1f23fc9Smlelstv error = copyout(&addr, recv->addr, sizeof(addr)); 2503b1f23fc9Smlelstv if (error == 0) 2504b1f23fc9Smlelstv error = copyout(&ccode, recv->msg.data, 1); 2505b1f23fc9Smlelstv if (error == 0) 2506b1f23fc9Smlelstv error = copyout(buf, recv->msg.data+1, len); 2507b1f23fc9Smlelstv break; 2508b1f23fc9Smlelstv case IPMICTL_SET_MY_ADDRESS_CMD: 2509b1f23fc9Smlelstv sc->sc_address = *(int *)data; 2510b1f23fc9Smlelstv break; 2511b1f23fc9Smlelstv case IPMICTL_GET_MY_ADDRESS_CMD: 2512b1f23fc9Smlelstv *(int *)data = sc->sc_address; 2513b1f23fc9Smlelstv break; 2514b1f23fc9Smlelstv case IPMICTL_SET_MY_LUN_CMD: 2515b1f23fc9Smlelstv sc->sc_lun = *(int *)data & 0x3; 2516b1f23fc9Smlelstv break; 2517b1f23fc9Smlelstv case IPMICTL_GET_MY_LUN_CMD: 2518b1f23fc9Smlelstv *(int *)data = sc->sc_lun; 2519b1f23fc9Smlelstv break; 2520b1f23fc9Smlelstv case IPMICTL_SET_GETS_EVENTS_CMD: 2521b1f23fc9Smlelstv break; 2522b1f23fc9Smlelstv case IPMICTL_REGISTER_FOR_CMD: 2523b1f23fc9Smlelstv case IPMICTL_UNREGISTER_FOR_CMD: 2524b1f23fc9Smlelstv error = EOPNOTSUPP; 2525b1f23fc9Smlelstv break; 2526b1f23fc9Smlelstv default: 2527b1f23fc9Smlelstv error = ENODEV; 2528b1f23fc9Smlelstv break; 2529b1f23fc9Smlelstv } 2530b1f23fc9Smlelstv 2531b1f23fc9Smlelstv if (buf) 2532b1f23fc9Smlelstv free(buf, M_DEVBUF); 2533b1f23fc9Smlelstv 2534b1f23fc9Smlelstv mutex_exit(&sc->sc_cmd_mtx); 2535b1f23fc9Smlelstv 2536b1f23fc9Smlelstv switch (cmd) { 2537b1f23fc9Smlelstv case IPMICTL_RECEIVE_MSG: 2538b1f23fc9Smlelstv case IPMICTL_RECEIVE_MSG_TRUNC: 2539b1f23fc9Smlelstv mutex_enter(&sc->sc_poll_mtx); 2540b1f23fc9Smlelstv sc->sc_mode = IPMI_MODE_IDLE; 2541b1f23fc9Smlelstv cv_broadcast(&sc->sc_mode_cv); 2542b1f23fc9Smlelstv mutex_exit(&sc->sc_poll_mtx); 2543b1f23fc9Smlelstv break; 2544b1f23fc9Smlelstv } 2545b1f23fc9Smlelstv 2546b1f23fc9Smlelstv return error; 2547b1f23fc9Smlelstv } 2548b1f23fc9Smlelstv 2549b1f23fc9Smlelstv static int 2550b1f23fc9Smlelstv ipmi_poll(dev_t dev, int events, lwp_t *l) 2551b1f23fc9Smlelstv { 2552b1f23fc9Smlelstv struct ipmi_softc *sc; 2553b1f23fc9Smlelstv int unit, revents = 0; 2554b1f23fc9Smlelstv 2555b1f23fc9Smlelstv unit = IPMIUNIT(dev); 2556b1f23fc9Smlelstv if ((sc = device_lookup_private(&ipmi_cd, unit)) == NULL) 2557b1f23fc9Smlelstv return (ENXIO); 2558b1f23fc9Smlelstv 2559b1f23fc9Smlelstv mutex_enter(&sc->sc_cmd_mtx); 2560b1f23fc9Smlelstv if (events & (POLLIN | POLLRDNORM)) { 2561b1f23fc9Smlelstv if (sc->sc_sent) 2562b1f23fc9Smlelstv revents |= events & (POLLIN | POLLRDNORM); 2563b1f23fc9Smlelstv } 2564b1f23fc9Smlelstv mutex_exit(&sc->sc_cmd_mtx); 2565b1f23fc9Smlelstv 2566b1f23fc9Smlelstv return revents; 2567b1f23fc9Smlelstv } 2568