xref: /openbsd-src/sys/dev/i2c/ipmi_i2c.c (revision 5a38ef86d0b61900239c7913d24a05e7b88a58f0)
1 /*	$OpenBSD: ipmi_i2c.c,v 1.3 2020/01/11 20:41:34 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_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 
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[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 
253 release:
254 	iic_release_bus(sc->sc_tag, 0);
255 
256 	return (error ? -1 : 0);
257 }
258 
259 int
260 ssif_reset(struct ipmi_softc *sc)
261 {
262 	return -1;
263 }
264 
265 int
266 ssif_probe(struct ipmi_softc *sc)
267 {
268 	return 0;
269 }
270