xref: /openbsd/sys/dev/i2c/ipmi_i2c.c (revision 274d7c50)
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