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
ipmi_i2c_match(struct device * parent,void * match,void * aux)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
ipmi_i2c_attach(struct device * parent,struct device * self,void * aux)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
ipmi_i2c_get_interface_caps(struct ipmi_i2c_softc * sc)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
ipmi_i2c_get_device_id(struct ipmi_i2c_softc * sc)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
ssif_sendmsg(struct ipmi_cmd * c)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
ssif_recvmsg(struct ipmi_cmd * c)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
ssif_reset(struct ipmi_softc * sc)259 ssif_reset(struct ipmi_softc *sc)
260 {
261 return -1;
262 }
263
264 int
ssif_probe(struct ipmi_softc * sc)265 ssif_probe(struct ipmi_softc *sc)
266 {
267 return 0;
268 }
269