1193323Sed/* $OpenBSD: ipmi_i2c.c,v 1.4 2022/04/06 18:59:28 naddy Exp $ */ 2193323Sed/* 3193323Sed * Copyright (c) 2019 Mark Kettenis <kettenis@openbsd.org> 4193323Sed * 5193323Sed * Permission to use, copy, modify, and distribute this software for any 6193323Sed * purpose with or without fee is hereby granted, provided that the above 7193323Sed * copyright notice and this permission notice appear in all copies. 8193323Sed * 9193323Sed * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10193323Sed * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11193323Sed * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12193323Sed * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13193323Sed * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14193323Sed * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15193323Sed * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16193323Sed */ 17193323Sed 18193323Sed#include <sys/param.h> 19193323Sed#include <sys/systm.h> 20193323Sed#include <sys/device.h> 21193323Sed 22193323Sed#include <machine/bus.h> 23198090Srdivacky 24193323Sed#include <dev/i2c/i2cvar.h> 25193323Sed#include <dev/ipmivar.h> 26193323Sed 27193323Sed#define BMC_SA 0x20 /* BMC/ESM3 */ 28193323Sed#define BMC_LUN 0 29198090Srdivacky 30198090Srdivackystruct ipmi_i2c_softc { 31198090Srdivacky struct ipmi_softc sc; 32198090Srdivacky i2c_tag_t sc_tag; 33198090Srdivacky i2c_addr_t sc_addr; 34198090Srdivacky uint8_t sc_rev; 35198090Srdivacky}; 36198090Srdivacky 37198090Srdivackyvoid cmn_buildmsg(struct ipmi_cmd *); 38198090Srdivackyint ssif_sendmsg(struct ipmi_cmd *); 39193323Sedint ssif_recvmsg(struct ipmi_cmd *); 40193323Sedint ssif_reset(struct ipmi_softc *); 41193323Sedint ssif_probe(struct ipmi_softc *); 42193323Sed 43193323Sedstruct ipmi_if ssif_if = { 44193323Sed "SSIF", 45193323Sed 0, 46193323Sed cmn_buildmsg, 47198090Srdivacky ssif_sendmsg, 48198090Srdivacky ssif_recvmsg, 49198090Srdivacky ssif_reset, 50198090Srdivacky ssif_probe, 51198090Srdivacky IPMI_MSG_DATASND, 52198090Srdivacky IPMI_MSG_DATARCV 53198090Srdivacky}; 54198090Srdivacky 55198090Srdivackyextern void ipmi_attach(struct device *, struct device *, void *); 56198090Srdivacky 57198090Srdivackyint ipmi_i2c_match(struct device *, void *, void *); 58198090Srdivackyvoid ipmi_i2c_attach(struct device *, struct device *, void *); 59198090Srdivacky 60198090Srdivackyconst struct cfattach ipmi_i2c_ca = { 61198090Srdivacky sizeof(struct ipmi_i2c_softc), ipmi_i2c_match, ipmi_i2c_attach 62198090Srdivacky}; 63198090Srdivacky 64198090Srdivackyint ipmi_i2c_get_interface_caps(struct ipmi_i2c_softc *); 65198090Srdivackyint ipmi_i2c_get_device_id(struct ipmi_i2c_softc *); 66198090Srdivacky 67198090Srdivackyint 68198090Srdivackyipmi_i2c_match(struct device *parent, void *match, void *aux) 69198090Srdivacky{ 70198090Srdivacky struct i2c_attach_args *ia = aux; 71198090Srdivacky 72198090Srdivacky return (strcmp(ia->ia_name, "IPI0001") == 0 || 73198090Srdivacky strcmp(ia->ia_name, "APMC0D8A") == 0); 74198090Srdivacky} 75198090Srdivacky 76198090Srdivackyvoid 77198090Srdivackyipmi_i2c_attach(struct device *parent, struct device *self, void *aux) 78198090Srdivacky{ 79198090Srdivacky struct ipmi_i2c_softc *sc = (struct ipmi_i2c_softc *)self; 80198090Srdivacky struct i2c_attach_args *ia = aux; 81198090Srdivacky struct ipmi_attach_args iaa; 82198090Srdivacky 83198090Srdivacky sc->sc_tag = ia->ia_tag; 84198090Srdivacky sc->sc_addr = ia->ia_addr; 85198090Srdivacky sc->sc.sc_if = &ssif_if; 86198090Srdivacky 87198090Srdivacky if (ipmi_i2c_get_interface_caps(sc)) { 88198090Srdivacky printf(": can't get system interface capabilities\n"); 89198090Srdivacky return; 90198090Srdivacky } 91198090Srdivacky 92198090Srdivacky if (ipmi_i2c_get_device_id(sc)) { 93198090Srdivacky printf(": can't get system interface capabilities\n"); 94198090Srdivacky return; 95198090Srdivacky } 96198090Srdivacky 97198090Srdivacky memset(&iaa, 0, sizeof(iaa)); 98198090Srdivacky iaa.iaa_if_type = IPMI_IF_SSIF; 99198090Srdivacky iaa.iaa_if_rev = (sc->sc_rev >> 4 | sc->sc_rev << 4); 100198090Srdivacky iaa.iaa_if_irq = -1; 101198090Srdivacky ipmi_attach_common(&sc->sc, &iaa); 102198090Srdivacky} 103198090Srdivacky 104198090Srdivackyint 105198090Srdivackyipmi_i2c_get_interface_caps(struct ipmi_i2c_softc *sc) 106198090Srdivacky{ 107198090Srdivacky struct ipmi_cmd c; 108198090Srdivacky uint8_t data[5]; 109198090Srdivacky 110198090Srdivacky data[0] = 0; /* SSIF */ 111198090Srdivacky 112198090Srdivacky c.c_sc = &sc->sc; 113198090Srdivacky c.c_rssa = BMC_SA; 114198090Srdivacky c.c_rslun = BMC_LUN; 115198090Srdivacky c.c_netfn = APP_NETFN; 116198090Srdivacky c.c_cmd = APP_GET_SYSTEM_INTERFACE_CAPS; 117198090Srdivacky c.c_txlen = 1; 118198090Srdivacky c.c_rxlen = 0; 119198090Srdivacky c.c_maxrxlen = sizeof(data); 120198090Srdivacky c.c_data = data; 121198090Srdivacky if (ipmi_sendcmd(&c) || ipmi_recvcmd(&c)) 122198090Srdivacky return EIO; 123198090Srdivacky 124198090Srdivacky /* Check SSIF version number. */ 125198090Srdivacky if ((data[1] & 0x7) != 0) 126198090Srdivacky return EINVAL; 127198090Srdivacky /* Check input and output message sizes. */ 128198090Srdivacky if (data[2] < 32 || data[3] < 32) 129198090Srdivacky return EINVAL; 130198090Srdivacky 131198090Srdivacky return 0; 132198090Srdivacky} 133198090Srdivacky 134198090Srdivackyint 135198090Srdivackyipmi_i2c_get_device_id(struct ipmi_i2c_softc *sc) 136198090Srdivacky{ 137198090Srdivacky struct ipmi_cmd c; 138198090Srdivacky uint8_t data[16]; 139198090Srdivacky 140198090Srdivacky c.c_sc = &sc->sc; 141198090Srdivacky c.c_rssa = BMC_SA; 142198090Srdivacky c.c_rslun = BMC_LUN; 143198090Srdivacky c.c_netfn = APP_NETFN; 144198090Srdivacky c.c_cmd = APP_GET_DEVICE_ID; 145198090Srdivacky c.c_txlen = 0; 146198090Srdivacky c.c_rxlen = 0; 147198090Srdivacky c.c_maxrxlen = sizeof(data); 148198090Srdivacky c.c_data = data; 149198090Srdivacky if (ipmi_sendcmd(&c) || ipmi_recvcmd(&c)) 150198090Srdivacky return EIO; 151198090Srdivacky 152198090Srdivacky sc->sc_rev = data[4]; 153198090Srdivacky return 0; 154198090Srdivacky} 155198090Srdivacky 156198090Srdivackyint 157198090Srdivackyssif_sendmsg(struct ipmi_cmd *c) 158198090Srdivacky{ 159198090Srdivacky struct ipmi_i2c_softc *sc = (struct ipmi_i2c_softc *)c->c_sc; 160198090Srdivacky uint8_t *buf = sc->sc.sc_buf; 161198090Srdivacky uint8_t cmd[2]; 162198090Srdivacky int error, retry; 163198090Srdivacky 164198090Srdivacky /* We only support single-part writes. */ 165198090Srdivacky if (c->c_txlen > 32) 166198090Srdivacky return -1; 167198090Srdivacky 168198090Srdivacky iic_acquire_bus(sc->sc_tag, 0); 169198090Srdivacky 170198090Srdivacky cmd[0] = 2; 171198090Srdivacky cmd[1] = c->c_txlen; 172198090Srdivacky for (retry = 0; retry < 5; retry++) { 173198090Srdivacky error = iic_exec(sc->sc_tag, I2C_OP_WRITE_BLOCK, 174198090Srdivacky sc->sc_addr, cmd, sizeof(cmd), buf, c->c_txlen, 0); 175198090Srdivacky if (!error) 176198090Srdivacky break; 177198090Srdivacky } 178198090Srdivacky 179198090Srdivacky iic_release_bus(sc->sc_tag, 0); 180198090Srdivacky 181198090Srdivacky return (error ? -1 : 0); 182198090Srdivacky} 183198090Srdivacky 184198090Srdivackyint 185198090Srdivackyssif_recvmsg(struct ipmi_cmd *c) 186198090Srdivacky{ 187198090Srdivacky struct ipmi_i2c_softc *sc = (struct ipmi_i2c_softc *)c->c_sc; 188198090Srdivacky uint8_t buf[33]; 189198090Srdivacky uint8_t cmd[1]; 190198090Srdivacky uint8_t len; 191198090Srdivacky int error, retry; 192198090Srdivacky int blkno = 0; 193198090Srdivacky 194198090Srdivacky iic_acquire_bus(sc->sc_tag, 0); 195198090Srdivacky 196198090Srdivacky cmd[0] = 3; 197198090Srdivacky for (retry = 0; retry < 250; retry++) { 198198090Srdivacky memset(buf, 0, sizeof(buf)); 199198090Srdivacky error = iic_exec(sc->sc_tag, I2C_OP_READ_BLOCK, 200198090Srdivacky sc->sc_addr, cmd, sizeof(cmd), buf, sizeof(buf), 0); 201198090Srdivacky if (error) 202198090Srdivacky continue; 203198090Srdivacky 204198090Srdivacky if (buf[0] < 1 || buf[0] > 32) { 205198090Srdivacky error = EIO; 206198090Srdivacky goto release; 207198090Srdivacky } 208198090Srdivacky 209198090Srdivacky if (buf[0] == 32 && buf[1] == 0x00 && buf[2] == 0x01) { 210198090Srdivacky /* Multi-part read start. */ 211198090Srdivacky c->c_rxlen = MIN(30, c->c_maxrxlen); 212198090Srdivacky memcpy(sc->sc.sc_buf, &buf[3], c->c_rxlen); 213198090Srdivacky break; 214198090Srdivacky } else { 215198090Srdivacky /* Single-part read. */ 216198090Srdivacky c->c_rxlen = MIN(buf[0], c->c_maxrxlen); 217198090Srdivacky memcpy(sc->sc.sc_buf, &buf[1], c->c_rxlen); 218198090Srdivacky goto release; 219198090Srdivacky } 220198090Srdivacky } 221198090Srdivacky if (retry == 250) 222198090Srdivacky goto release; 223198090Srdivacky 224198090Srdivacky cmd[0] = 9; 225198090Srdivacky while (buf[1] != 0xff && c->c_rxlen < c->c_maxrxlen) { 226198090Srdivacky memset(buf, 0, sizeof(buf)); 227198090Srdivacky error = iic_exec(sc->sc_tag, I2C_OP_READ_BLOCK, 228198090Srdivacky sc->sc_addr, cmd, sizeof(cmd), buf, sizeof(buf), 0); 229198090Srdivacky if (error) 230198090Srdivacky goto release; 231198090Srdivacky 232198090Srdivacky if (buf[0] < 1 || buf[0] > 32) { 233198090Srdivacky error = EIO; 234198090Srdivacky goto release; 235198090Srdivacky } 236198090Srdivacky 237198090Srdivacky if (buf[0] == 32 && buf[1] == blkno) { 238198090Srdivacky /* Multi-part read middle. */ 239198090Srdivacky len = MIN(31, c->c_maxrxlen - c->c_rxlen); 240198090Srdivacky memcpy(&sc->sc.sc_buf[c->c_rxlen], &buf[2], len); 241198090Srdivacky } else if (buf[1] == 0xff) { 242198090Srdivacky /* Multi-part read end. */ 243198090Srdivacky len = MIN(buf[0] - 1, c->c_maxrxlen - c->c_rxlen); 244198090Srdivacky memcpy(&sc->sc.sc_buf[c->c_rxlen], &buf[2], len); 245198090Srdivacky } else { 246198090Srdivacky error = EIO; 247198090Srdivacky goto release; 248198090Srdivacky } 249198090Srdivacky c->c_rxlen += len; 250198090Srdivacky blkno++; 251198090Srdivacky } 252198090Srdivacky 253198090Srdivackyrelease: 254198090Srdivacky iic_release_bus(sc->sc_tag, 0); 255198090Srdivacky 256198090Srdivacky return (error ? -1 : 0); 257198090Srdivacky} 258198090Srdivacky 259198090Srdivackyint 260198090Srdivackyssif_reset(struct ipmi_softc *sc) 261198090Srdivacky{ 262198090Srdivacky return -1; 263198090Srdivacky} 264198090Srdivacky 265198090Srdivackyint 266199481Srdivackyssif_probe(struct ipmi_softc *sc) 267199481Srdivacky{ 268198090Srdivacky return 0; 269198090Srdivacky} 270198090Srdivacky