1/* $OpenBSD: ipmi_i2c.c,v 1.4 2022/04/06 18:59:28 naddy Exp $ */ 2/* 3 * Copyright (c) 2019 Mark Kettenis <kettenis@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18#include <sys/param.h> 19#include <sys/systm.h> 20#include <sys/device.h> 21 22#include <machine/bus.h> 23 24#include <dev/i2c/i2cvar.h> 25#include <dev/ipmivar.h> 26 27#define BMC_SA 0x20 /* BMC/ESM3 */ 28#define BMC_LUN 0 29 30struct ipmi_i2c_softc { 31 struct ipmi_softc sc; 32 i2c_tag_t sc_tag; 33 i2c_addr_t sc_addr; 34 uint8_t sc_rev; 35}; 36 37void cmn_buildmsg(struct ipmi_cmd *); 38int ssif_sendmsg(struct ipmi_cmd *); 39int ssif_recvmsg(struct ipmi_cmd *); 40int ssif_reset(struct ipmi_softc *); 41int ssif_probe(struct ipmi_softc *); 42 43struct ipmi_if ssif_if = { 44 "SSIF", 45 0, 46 cmn_buildmsg, 47 ssif_sendmsg, 48 ssif_recvmsg, 49 ssif_reset, 50 ssif_probe, 51 IPMI_MSG_DATASND, 52 IPMI_MSG_DATARCV 53}; 54 55extern void ipmi_attach(struct device *, struct device *, void *); 56 57int ipmi_i2c_match(struct device *, void *, void *); 58void ipmi_i2c_attach(struct device *, struct device *, void *); 59 60const struct cfattach ipmi_i2c_ca = { 61 sizeof(struct ipmi_i2c_softc), ipmi_i2c_match, ipmi_i2c_attach 62}; 63 64int ipmi_i2c_get_interface_caps(struct ipmi_i2c_softc *); 65int ipmi_i2c_get_device_id(struct ipmi_i2c_softc *); 66 67int 68ipmi_i2c_match(struct device *parent, void *match, void *aux) 69{ 70 struct i2c_attach_args *ia = aux; 71 72 return (strcmp(ia->ia_name, "IPI0001") == 0 || 73 strcmp(ia->ia_name, "APMC0D8A") == 0); 74} 75 76void 77ipmi_i2c_attach(struct device *parent, struct device *self, void *aux) 78{ 79 struct ipmi_i2c_softc *sc = (struct ipmi_i2c_softc *)self; 80 struct i2c_attach_args *ia = aux; 81 struct ipmi_attach_args iaa; 82 83 sc->sc_tag = ia->ia_tag; 84 sc->sc_addr = ia->ia_addr; 85 sc->sc.sc_if = &ssif_if; 86 87 if (ipmi_i2c_get_interface_caps(sc)) { 88 printf(": can't get system interface capabilities\n"); 89 return; 90 } 91 92 if (ipmi_i2c_get_device_id(sc)) { 93 printf(": can't get system interface capabilities\n"); 94 return; 95 } 96 97 memset(&iaa, 0, sizeof(iaa)); 98 iaa.iaa_if_type = IPMI_IF_SSIF; 99 iaa.iaa_if_rev = (sc->sc_rev >> 4 | sc->sc_rev << 4); 100 iaa.iaa_if_irq = -1; 101 ipmi_attach_common(&sc->sc, &iaa); 102} 103 104int 105ipmi_i2c_get_interface_caps(struct ipmi_i2c_softc *sc) 106{ 107 struct ipmi_cmd c; 108 uint8_t data[5]; 109 110 data[0] = 0; /* SSIF */ 111 112 c.c_sc = &sc->sc; 113 c.c_rssa = BMC_SA; 114 c.c_rslun = BMC_LUN; 115 c.c_netfn = APP_NETFN; 116 c.c_cmd = APP_GET_SYSTEM_INTERFACE_CAPS; 117 c.c_txlen = 1; 118 c.c_rxlen = 0; 119 c.c_maxrxlen = sizeof(data); 120 c.c_data = data; 121 if (ipmi_sendcmd(&c) || ipmi_recvcmd(&c)) 122 return EIO; 123 124 /* Check SSIF version number. */ 125 if ((data[1] & 0x7) != 0) 126 return EINVAL; 127 /* Check input and output message sizes. */ 128 if (data[2] < 32 || data[3] < 32) 129 return EINVAL; 130 131 return 0; 132} 133 134int 135ipmi_i2c_get_device_id(struct ipmi_i2c_softc *sc) 136{ 137 struct ipmi_cmd c; 138 uint8_t data[16]; 139 140 c.c_sc = &sc->sc; 141 c.c_rssa = BMC_SA; 142 c.c_rslun = BMC_LUN; 143 c.c_netfn = APP_NETFN; 144 c.c_cmd = APP_GET_DEVICE_ID; 145 c.c_txlen = 0; 146 c.c_rxlen = 0; 147 c.c_maxrxlen = sizeof(data); 148 c.c_data = data; 149 if (ipmi_sendcmd(&c) || ipmi_recvcmd(&c)) 150 return EIO; 151 152 sc->sc_rev = data[4]; 153 return 0; 154} 155 156int 157ssif_sendmsg(struct ipmi_cmd *c) 158{ 159 struct ipmi_i2c_softc *sc = (struct ipmi_i2c_softc *)c->c_sc; 160 uint8_t *buf = sc->sc.sc_buf; 161 uint8_t cmd[2]; 162 int error, retry; 163 164 /* We only support single-part writes. */ 165 if (c->c_txlen > 32) 166 return -1; 167 168 iic_acquire_bus(sc->sc_tag, 0); 169 170 cmd[0] = 2; 171 cmd[1] = c->c_txlen; 172 for (retry = 0; retry < 5; retry++) { 173 error = iic_exec(sc->sc_tag, I2C_OP_WRITE_BLOCK, 174 sc->sc_addr, cmd, sizeof(cmd), buf, c->c_txlen, 0); 175 if (!error) 176 break; 177 } 178 179 iic_release_bus(sc->sc_tag, 0); 180 181 return (error ? -1 : 0); 182} 183 184int 185ssif_recvmsg(struct ipmi_cmd *c) 186{ 187 struct ipmi_i2c_softc *sc = (struct ipmi_i2c_softc *)c->c_sc; 188 uint8_t buf[33]; 189 uint8_t cmd[1]; 190 uint8_t len; 191 int error, retry; 192 int blkno = 0; 193 194 iic_acquire_bus(sc->sc_tag, 0); 195 196 cmd[0] = 3; 197 for (retry = 0; retry < 250; retry++) { 198 memset(buf, 0, sizeof(buf)); 199 error = iic_exec(sc->sc_tag, I2C_OP_READ_BLOCK, 200 sc->sc_addr, cmd, sizeof(cmd), buf, sizeof(buf), 0); 201 if (error) 202 continue; 203 204 if (buf[0] < 1 || buf[0] > 32) { 205 error = EIO; 206 goto release; 207 } 208 209 if (buf[0] == 32 && buf[1] == 0x00 && buf[2] == 0x01) { 210 /* Multi-part read start. */ 211 c->c_rxlen = MIN(30, c->c_maxrxlen); 212 memcpy(sc->sc.sc_buf, &buf[3], c->c_rxlen); 213 break; 214 } else { 215 /* Single-part read. */ 216 c->c_rxlen = MIN(buf[0], c->c_maxrxlen); 217 memcpy(sc->sc.sc_buf, &buf[1], c->c_rxlen); 218 goto release; 219 } 220 } 221 if (retry == 250) 222 goto release; 223 224 cmd[0] = 9; 225 while (buf[1] != 0xff && c->c_rxlen < c->c_maxrxlen) { 226 memset(buf, 0, sizeof(buf)); 227 error = iic_exec(sc->sc_tag, I2C_OP_READ_BLOCK, 228 sc->sc_addr, cmd, sizeof(cmd), buf, sizeof(buf), 0); 229 if (error) 230 goto release; 231 232 if (buf[0] < 1 || buf[0] > 32) { 233 error = EIO; 234 goto release; 235 } 236 237 if (buf[0] == 32 && buf[1] == blkno) { 238 /* Multi-part read middle. */ 239 len = MIN(31, c->c_maxrxlen - c->c_rxlen); 240 memcpy(&sc->sc.sc_buf[c->c_rxlen], &buf[2], len); 241 } else if (buf[1] == 0xff) { 242 /* Multi-part read end. */ 243 len = MIN(buf[0] - 1, c->c_maxrxlen - c->c_rxlen); 244 memcpy(&sc->sc.sc_buf[c->c_rxlen], &buf[2], len); 245 } else { 246 error = EIO; 247 goto release; 248 } 249 c->c_rxlen += len; 250 blkno++; 251 } 252 253release: 254 iic_release_bus(sc->sc_tag, 0); 255 256 return (error ? -1 : 0); 257} 258 259int 260ssif_reset(struct ipmi_softc *sc) 261{ 262 return -1; 263} 264 265int 266ssif_probe(struct ipmi_softc *sc) 267{ 268 return 0; 269} 270