1 /* $OpenBSD: ipmi_i2c.c,v 1.6 2024/10/09 00:38:26 jsg 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 30 struct 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 37 void cmn_buildmsg(struct ipmi_cmd *); 38 int ssif_sendmsg(struct ipmi_cmd *); 39 int ssif_recvmsg(struct ipmi_cmd *); 40 int ssif_reset(struct ipmi_softc *); 41 int ssif_probe(struct ipmi_softc *); 42 43 struct 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 55 int ipmi_i2c_match(struct device *, void *, void *); 56 void ipmi_i2c_attach(struct device *, struct device *, void *); 57 58 const struct cfattach ipmi_i2c_ca = { 59 sizeof(struct ipmi_i2c_softc), ipmi_i2c_match, ipmi_i2c_attach, 60 NULL, ipmi_activate 61 }; 62 63 int ipmi_i2c_get_interface_caps(struct ipmi_i2c_softc *); 64 int ipmi_i2c_get_device_id(struct ipmi_i2c_softc *); 65 66 int 67 ipmi_i2c_match(struct device *parent, void *match, void *aux) 68 { 69 struct i2c_attach_args *ia = aux; 70 71 return (strcmp(ia->ia_name, "IPI0001") == 0 || 72 strcmp(ia->ia_name, "APMC0D8A") == 0); 73 } 74 75 void 76 ipmi_i2c_attach(struct device *parent, struct device *self, void *aux) 77 { 78 struct ipmi_i2c_softc *sc = (struct ipmi_i2c_softc *)self; 79 struct i2c_attach_args *ia = aux; 80 struct ipmi_attach_args iaa; 81 82 sc->sc_tag = ia->ia_tag; 83 sc->sc_addr = ia->ia_addr; 84 sc->sc.sc_if = &ssif_if; 85 86 if (ipmi_i2c_get_interface_caps(sc)) { 87 printf(": can't get system interface capabilities\n"); 88 return; 89 } 90 91 if (ipmi_i2c_get_device_id(sc)) { 92 printf(": can't get system interface capabilities\n"); 93 return; 94 } 95 96 memset(&iaa, 0, sizeof(iaa)); 97 iaa.iaa_if_type = IPMI_IF_SSIF; 98 iaa.iaa_if_rev = (sc->sc_rev >> 4 | sc->sc_rev << 4); 99 iaa.iaa_if_irq = -1; 100 ipmi_attach_common(&sc->sc, &iaa); 101 } 102 103 int 104 ipmi_i2c_get_interface_caps(struct ipmi_i2c_softc *sc) 105 { 106 struct ipmi_cmd c; 107 uint8_t data[5]; 108 109 data[0] = 0; /* SSIF */ 110 111 c.c_sc = &sc->sc; 112 c.c_rssa = BMC_SA; 113 c.c_rslun = BMC_LUN; 114 c.c_netfn = APP_NETFN; 115 c.c_cmd = APP_GET_SYSTEM_INTERFACE_CAPS; 116 c.c_txlen = 1; 117 c.c_rxlen = 0; 118 c.c_maxrxlen = sizeof(data); 119 c.c_data = data; 120 if (ipmi_sendcmd(&c) || ipmi_recvcmd(&c)) 121 return EIO; 122 123 /* Check SSIF version number. */ 124 if ((data[1] & 0x7) != 0) 125 return EINVAL; 126 /* Check input and output message sizes. */ 127 if (data[2] < 32 || data[3] < 32) 128 return EINVAL; 129 130 return 0; 131 } 132 133 int 134 ipmi_i2c_get_device_id(struct ipmi_i2c_softc *sc) 135 { 136 struct ipmi_cmd c; 137 uint8_t data[16]; 138 139 c.c_sc = &sc->sc; 140 c.c_rssa = BMC_SA; 141 c.c_rslun = BMC_LUN; 142 c.c_netfn = APP_NETFN; 143 c.c_cmd = APP_GET_DEVICE_ID; 144 c.c_txlen = 0; 145 c.c_rxlen = 0; 146 c.c_maxrxlen = sizeof(data); 147 c.c_data = data; 148 if (ipmi_sendcmd(&c) || ipmi_recvcmd(&c)) 149 return EIO; 150 151 sc->sc_rev = data[4]; 152 return 0; 153 } 154 155 int 156 ssif_sendmsg(struct ipmi_cmd *c) 157 { 158 struct ipmi_i2c_softc *sc = (struct ipmi_i2c_softc *)c->c_sc; 159 uint8_t *buf = sc->sc.sc_buf; 160 uint8_t cmd[2]; 161 int error, retry; 162 163 /* We only support single-part writes. */ 164 if (c->c_txlen > 32) 165 return -1; 166 167 iic_acquire_bus(sc->sc_tag, 0); 168 169 cmd[0] = 2; 170 cmd[1] = c->c_txlen; 171 for (retry = 0; retry < 5; retry++) { 172 error = iic_exec(sc->sc_tag, I2C_OP_WRITE_BLOCK, 173 sc->sc_addr, cmd, sizeof(cmd), buf, c->c_txlen, 0); 174 if (!error) 175 break; 176 } 177 178 iic_release_bus(sc->sc_tag, 0); 179 180 return (error ? -1 : 0); 181 } 182 183 int 184 ssif_recvmsg(struct ipmi_cmd *c) 185 { 186 struct ipmi_i2c_softc *sc = (struct ipmi_i2c_softc *)c->c_sc; 187 uint8_t buf[33]; 188 uint8_t cmd[1]; 189 uint8_t len; 190 int error, retry; 191 int blkno = 0; 192 193 iic_acquire_bus(sc->sc_tag, 0); 194 195 cmd[0] = 3; 196 for (retry = 0; retry < 250; retry++) { 197 memset(buf, 0, sizeof(buf)); 198 error = iic_exec(sc->sc_tag, I2C_OP_READ_BLOCK, 199 sc->sc_addr, cmd, sizeof(cmd), buf, sizeof(buf), 0); 200 if (error) 201 continue; 202 203 if (buf[0] < 1 || buf[0] > 32) { 204 error = EIO; 205 goto release; 206 } 207 208 if (buf[0] == 32 && buf[1] == 0x00 && buf[2] == 0x01) { 209 /* Multi-part read start. */ 210 c->c_rxlen = MIN(30, c->c_maxrxlen); 211 memcpy(sc->sc.sc_buf, &buf[3], c->c_rxlen); 212 break; 213 } else { 214 /* Single-part read. */ 215 c->c_rxlen = MIN(buf[0], c->c_maxrxlen); 216 memcpy(sc->sc.sc_buf, &buf[1], c->c_rxlen); 217 goto release; 218 } 219 } 220 if (retry == 250) 221 goto release; 222 223 cmd[0] = 9; 224 while (buf[1] != 0xff && c->c_rxlen < c->c_maxrxlen) { 225 memset(buf, 0, sizeof(buf)); 226 error = iic_exec(sc->sc_tag, I2C_OP_READ_BLOCK, 227 sc->sc_addr, cmd, sizeof(cmd), buf, sizeof(buf), 0); 228 if (error) 229 goto release; 230 231 if (buf[0] < 1 || buf[0] > 32) { 232 error = EIO; 233 goto release; 234 } 235 236 if (buf[0] == 32 && buf[1] == blkno) { 237 /* Multi-part read middle. */ 238 len = MIN(31, c->c_maxrxlen - c->c_rxlen); 239 memcpy(&sc->sc.sc_buf[c->c_rxlen], &buf[2], len); 240 } else if (buf[1] == 0xff) { 241 /* Multi-part read end. */ 242 len = MIN(buf[0] - 1, c->c_maxrxlen - c->c_rxlen); 243 memcpy(&sc->sc.sc_buf[c->c_rxlen], &buf[2], len); 244 } else { 245 error = EIO; 246 goto release; 247 } 248 c->c_rxlen += len; 249 blkno++; 250 } 251 252 release: 253 iic_release_bus(sc->sc_tag, 0); 254 255 return (error ? -1 : 0); 256 } 257 258 int 259 ssif_reset(struct ipmi_softc *sc) 260 { 261 return -1; 262 } 263 264 int 265 ssif_probe(struct ipmi_softc *sc) 266 { 267 return 0; 268 } 269