1 /* $OpenBSD: ipmi_i2c.c,v 1.2 2019/09/03 09:17:10 kettenis 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 extern void ipmi_attach(struct device *, struct device *, void *); 56 57 int ipmi_i2c_match(struct device *, void *, void *); 58 void ipmi_i2c_attach(struct device *, struct device *, void *); 59 60 struct cfattach ipmi_i2c_ca = { 61 sizeof(struct ipmi_i2c_softc), ipmi_i2c_match, ipmi_i2c_attach 62 }; 63 64 int ipmi_i2c_get_interface_caps(struct ipmi_i2c_softc *); 65 int ipmi_i2c_get_device_id(struct ipmi_i2c_softc *); 66 67 int 68 ipmi_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 76 void 77 ipmi_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 104 int 105 ipmi_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 134 int 135 ipmi_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 156 int 157 ssif_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_WITH_STOP, 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 184 int 185 ssif_recvmsg(struct ipmi_cmd *c) 186 { 187 struct ipmi_i2c_softc *sc = (struct ipmi_i2c_softc *)c->c_sc; 188 uint8_t buf[IPMI_MAX_RX + 16]; 189 uint8_t cmd[1]; 190 int error, retry; 191 192 /* We only support single-part reads. */ 193 if (c->c_maxrxlen > 32) 194 return -1; 195 196 iic_acquire_bus(sc->sc_tag, 0); 197 198 cmd[0] = 3; 199 for (retry = 0; retry < 250; retry++) { 200 memset(buf, 0, c->c_maxrxlen + 1); 201 error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 202 sc->sc_addr, cmd, sizeof(cmd), buf, c->c_maxrxlen + 1, 0); 203 if (!error) { 204 c->c_rxlen = buf[0]; 205 memcpy(sc->sc.sc_buf, &buf[1], c->c_maxrxlen); 206 break; 207 } 208 } 209 210 iic_release_bus(sc->sc_tag, 0); 211 212 return (error ? -1 : 0); 213 } 214 215 int 216 ssif_reset(struct ipmi_softc *sc) 217 { 218 return -1; 219 } 220 221 int 222 ssif_probe(struct ipmi_softc *sc) 223 { 224 return 0; 225 } 226