1193323Sed/*	$OpenBSD: ipmi_i2c.c,v 1.4 2022/04/06 18:59:28 naddy Exp $	*/
2193323Sed/*
3193323Sed * Copyright (c) 2019 Mark Kettenis <kettenis@openbsd.org>
4193323Sed *
5193323Sed * Permission to use, copy, modify, and distribute this software for any
6193323Sed * purpose with or without fee is hereby granted, provided that the above
7193323Sed * copyright notice and this permission notice appear in all copies.
8193323Sed *
9193323Sed * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10193323Sed * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11193323Sed * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12193323Sed * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13193323Sed * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14193323Sed * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15193323Sed * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16193323Sed */
17193323Sed
18193323Sed#include <sys/param.h>
19193323Sed#include <sys/systm.h>
20193323Sed#include <sys/device.h>
21193323Sed
22193323Sed#include <machine/bus.h>
23198090Srdivacky
24193323Sed#include <dev/i2c/i2cvar.h>
25193323Sed#include <dev/ipmivar.h>
26193323Sed
27193323Sed#define BMC_SA			0x20	/* BMC/ESM3 */
28193323Sed#define BMC_LUN			0
29198090Srdivacky
30198090Srdivackystruct ipmi_i2c_softc {
31198090Srdivacky	struct ipmi_softc sc;
32198090Srdivacky	i2c_tag_t	sc_tag;
33198090Srdivacky	i2c_addr_t	sc_addr;
34198090Srdivacky	uint8_t		sc_rev;
35198090Srdivacky};
36198090Srdivacky
37198090Srdivackyvoid	cmn_buildmsg(struct ipmi_cmd *);
38198090Srdivackyint	ssif_sendmsg(struct ipmi_cmd *);
39193323Sedint	ssif_recvmsg(struct ipmi_cmd *);
40193323Sedint	ssif_reset(struct ipmi_softc *);
41193323Sedint	ssif_probe(struct ipmi_softc *);
42193323Sed
43193323Sedstruct ipmi_if ssif_if = {
44193323Sed	"SSIF",
45193323Sed	0,
46193323Sed	cmn_buildmsg,
47198090Srdivacky	ssif_sendmsg,
48198090Srdivacky	ssif_recvmsg,
49198090Srdivacky	ssif_reset,
50198090Srdivacky	ssif_probe,
51198090Srdivacky	IPMI_MSG_DATASND,
52198090Srdivacky	IPMI_MSG_DATARCV
53198090Srdivacky};
54198090Srdivacky
55198090Srdivackyextern void ipmi_attach(struct device *, struct device *, void *);
56198090Srdivacky
57198090Srdivackyint	ipmi_i2c_match(struct device *, void *, void *);
58198090Srdivackyvoid	ipmi_i2c_attach(struct device *, struct device *, void *);
59198090Srdivacky
60198090Srdivackyconst struct cfattach ipmi_i2c_ca = {
61198090Srdivacky	sizeof(struct ipmi_i2c_softc), ipmi_i2c_match, ipmi_i2c_attach
62198090Srdivacky};
63198090Srdivacky
64198090Srdivackyint	ipmi_i2c_get_interface_caps(struct ipmi_i2c_softc *);
65198090Srdivackyint	ipmi_i2c_get_device_id(struct ipmi_i2c_softc *);
66198090Srdivacky
67198090Srdivackyint
68198090Srdivackyipmi_i2c_match(struct device *parent, void *match, void *aux)
69198090Srdivacky{
70198090Srdivacky	struct i2c_attach_args *ia = aux;
71198090Srdivacky
72198090Srdivacky	return (strcmp(ia->ia_name, "IPI0001") == 0 ||
73198090Srdivacky	    strcmp(ia->ia_name, "APMC0D8A") == 0);
74198090Srdivacky}
75198090Srdivacky
76198090Srdivackyvoid
77198090Srdivackyipmi_i2c_attach(struct device *parent, struct device *self, void *aux)
78198090Srdivacky{
79198090Srdivacky	struct ipmi_i2c_softc *sc = (struct ipmi_i2c_softc *)self;
80198090Srdivacky	struct i2c_attach_args *ia = aux;
81198090Srdivacky	struct ipmi_attach_args iaa;
82198090Srdivacky
83198090Srdivacky	sc->sc_tag = ia->ia_tag;
84198090Srdivacky	sc->sc_addr = ia->ia_addr;
85198090Srdivacky	sc->sc.sc_if = &ssif_if;
86198090Srdivacky
87198090Srdivacky	if (ipmi_i2c_get_interface_caps(sc)) {
88198090Srdivacky		printf(": can't get system interface capabilities\n");
89198090Srdivacky		return;
90198090Srdivacky	}
91198090Srdivacky
92198090Srdivacky	if (ipmi_i2c_get_device_id(sc)) {
93198090Srdivacky		printf(": can't get system interface capabilities\n");
94198090Srdivacky		return;
95198090Srdivacky	}
96198090Srdivacky
97198090Srdivacky	memset(&iaa, 0, sizeof(iaa));
98198090Srdivacky	iaa.iaa_if_type = IPMI_IF_SSIF;
99198090Srdivacky	iaa.iaa_if_rev = (sc->sc_rev >> 4 | sc->sc_rev << 4);
100198090Srdivacky	iaa.iaa_if_irq = -1;
101198090Srdivacky	ipmi_attach_common(&sc->sc, &iaa);
102198090Srdivacky}
103198090Srdivacky
104198090Srdivackyint
105198090Srdivackyipmi_i2c_get_interface_caps(struct ipmi_i2c_softc *sc)
106198090Srdivacky{
107198090Srdivacky	struct ipmi_cmd c;
108198090Srdivacky	uint8_t data[5];
109198090Srdivacky
110198090Srdivacky	data[0] = 0;		/* SSIF */
111198090Srdivacky
112198090Srdivacky	c.c_sc = &sc->sc;
113198090Srdivacky	c.c_rssa = BMC_SA;
114198090Srdivacky	c.c_rslun = BMC_LUN;
115198090Srdivacky	c.c_netfn = APP_NETFN;
116198090Srdivacky	c.c_cmd = APP_GET_SYSTEM_INTERFACE_CAPS;
117198090Srdivacky	c.c_txlen = 1;
118198090Srdivacky	c.c_rxlen = 0;
119198090Srdivacky	c.c_maxrxlen = sizeof(data);
120198090Srdivacky	c.c_data = data;
121198090Srdivacky	if (ipmi_sendcmd(&c) || ipmi_recvcmd(&c))
122198090Srdivacky		return EIO;
123198090Srdivacky
124198090Srdivacky	/* Check SSIF version number. */
125198090Srdivacky	if ((data[1] & 0x7) != 0)
126198090Srdivacky		return EINVAL;
127198090Srdivacky	/* Check input and output message sizes. */
128198090Srdivacky	if (data[2] < 32 || data[3] < 32)
129198090Srdivacky		return EINVAL;
130198090Srdivacky
131198090Srdivacky	return 0;
132198090Srdivacky}
133198090Srdivacky
134198090Srdivackyint
135198090Srdivackyipmi_i2c_get_device_id(struct ipmi_i2c_softc *sc)
136198090Srdivacky{
137198090Srdivacky	struct ipmi_cmd c;
138198090Srdivacky	uint8_t data[16];
139198090Srdivacky
140198090Srdivacky	c.c_sc = &sc->sc;
141198090Srdivacky	c.c_rssa = BMC_SA;
142198090Srdivacky	c.c_rslun = BMC_LUN;
143198090Srdivacky	c.c_netfn = APP_NETFN;
144198090Srdivacky	c.c_cmd = APP_GET_DEVICE_ID;
145198090Srdivacky	c.c_txlen = 0;
146198090Srdivacky	c.c_rxlen = 0;
147198090Srdivacky	c.c_maxrxlen = sizeof(data);
148198090Srdivacky	c.c_data = data;
149198090Srdivacky	if (ipmi_sendcmd(&c) || ipmi_recvcmd(&c))
150198090Srdivacky		return EIO;
151198090Srdivacky
152198090Srdivacky	sc->sc_rev = data[4];
153198090Srdivacky	return 0;
154198090Srdivacky}
155198090Srdivacky
156198090Srdivackyint
157198090Srdivackyssif_sendmsg(struct ipmi_cmd *c)
158198090Srdivacky{
159198090Srdivacky	struct ipmi_i2c_softc *sc = (struct ipmi_i2c_softc *)c->c_sc;
160198090Srdivacky	uint8_t *buf = sc->sc.sc_buf;
161198090Srdivacky	uint8_t cmd[2];
162198090Srdivacky	int error, retry;
163198090Srdivacky
164198090Srdivacky	/* We only support single-part writes. */
165198090Srdivacky	if (c->c_txlen > 32)
166198090Srdivacky		return -1;
167198090Srdivacky
168198090Srdivacky	iic_acquire_bus(sc->sc_tag, 0);
169198090Srdivacky
170198090Srdivacky	cmd[0] = 2;
171198090Srdivacky	cmd[1] = c->c_txlen;
172198090Srdivacky	for (retry = 0; retry < 5; retry++) {
173198090Srdivacky		error = iic_exec(sc->sc_tag, I2C_OP_WRITE_BLOCK,
174198090Srdivacky		    sc->sc_addr, cmd, sizeof(cmd), buf, c->c_txlen, 0);
175198090Srdivacky		if (!error)
176198090Srdivacky			break;
177198090Srdivacky	}
178198090Srdivacky
179198090Srdivacky	iic_release_bus(sc->sc_tag, 0);
180198090Srdivacky
181198090Srdivacky	return (error ? -1 : 0);
182198090Srdivacky}
183198090Srdivacky
184198090Srdivackyint
185198090Srdivackyssif_recvmsg(struct ipmi_cmd *c)
186198090Srdivacky{
187198090Srdivacky	struct ipmi_i2c_softc *sc = (struct ipmi_i2c_softc *)c->c_sc;
188198090Srdivacky	uint8_t buf[33];
189198090Srdivacky	uint8_t cmd[1];
190198090Srdivacky	uint8_t len;
191198090Srdivacky	int error, retry;
192198090Srdivacky	int blkno = 0;
193198090Srdivacky
194198090Srdivacky	iic_acquire_bus(sc->sc_tag, 0);
195198090Srdivacky
196198090Srdivacky	cmd[0] = 3;
197198090Srdivacky	for (retry = 0; retry < 250; retry++) {
198198090Srdivacky		memset(buf, 0, sizeof(buf));
199198090Srdivacky		error = iic_exec(sc->sc_tag, I2C_OP_READ_BLOCK,
200198090Srdivacky		    sc->sc_addr, cmd, sizeof(cmd), buf, sizeof(buf), 0);
201198090Srdivacky		if (error)
202198090Srdivacky			continue;
203198090Srdivacky
204198090Srdivacky		if (buf[0] < 1 || buf[0] > 32) {
205198090Srdivacky			error = EIO;
206198090Srdivacky			goto release;
207198090Srdivacky		}
208198090Srdivacky
209198090Srdivacky		if (buf[0] == 32 && buf[1] == 0x00 && buf[2] == 0x01) {
210198090Srdivacky			/* Multi-part read start. */
211198090Srdivacky			c->c_rxlen = MIN(30, c->c_maxrxlen);
212198090Srdivacky			memcpy(sc->sc.sc_buf, &buf[3], c->c_rxlen);
213198090Srdivacky			break;
214198090Srdivacky		} else {
215198090Srdivacky			/* Single-part read. */
216198090Srdivacky			c->c_rxlen = MIN(buf[0], c->c_maxrxlen);
217198090Srdivacky			memcpy(sc->sc.sc_buf, &buf[1], c->c_rxlen);
218198090Srdivacky			goto release;
219198090Srdivacky		}
220198090Srdivacky	}
221198090Srdivacky	if (retry == 250)
222198090Srdivacky		goto release;
223198090Srdivacky
224198090Srdivacky	cmd[0] = 9;
225198090Srdivacky	while (buf[1] != 0xff && c->c_rxlen < c->c_maxrxlen) {
226198090Srdivacky		memset(buf, 0, sizeof(buf));
227198090Srdivacky		error = iic_exec(sc->sc_tag, I2C_OP_READ_BLOCK,
228198090Srdivacky		    sc->sc_addr, cmd, sizeof(cmd), buf, sizeof(buf), 0);
229198090Srdivacky		if (error)
230198090Srdivacky			goto release;
231198090Srdivacky
232198090Srdivacky		if (buf[0] < 1 || buf[0] > 32) {
233198090Srdivacky			error = EIO;
234198090Srdivacky			goto release;
235198090Srdivacky		}
236198090Srdivacky
237198090Srdivacky		if (buf[0] == 32 && buf[1] == blkno) {
238198090Srdivacky			/* Multi-part read middle. */
239198090Srdivacky			len = MIN(31, c->c_maxrxlen - c->c_rxlen);
240198090Srdivacky			memcpy(&sc->sc.sc_buf[c->c_rxlen], &buf[2], len);
241198090Srdivacky		} else if (buf[1] == 0xff) {
242198090Srdivacky			/* Multi-part read end. */
243198090Srdivacky			len = MIN(buf[0] - 1, c->c_maxrxlen - c->c_rxlen);
244198090Srdivacky			memcpy(&sc->sc.sc_buf[c->c_rxlen], &buf[2], len);
245198090Srdivacky		} else {
246198090Srdivacky			error = EIO;
247198090Srdivacky			goto release;
248198090Srdivacky		}
249198090Srdivacky		c->c_rxlen += len;
250198090Srdivacky		blkno++;
251198090Srdivacky	}
252198090Srdivacky
253198090Srdivackyrelease:
254198090Srdivacky	iic_release_bus(sc->sc_tag, 0);
255198090Srdivacky
256198090Srdivacky	return (error ? -1 : 0);
257198090Srdivacky}
258198090Srdivacky
259198090Srdivackyint
260198090Srdivackyssif_reset(struct ipmi_softc *sc)
261198090Srdivacky{
262198090Srdivacky	return -1;
263198090Srdivacky}
264198090Srdivacky
265198090Srdivackyint
266199481Srdivackyssif_probe(struct ipmi_softc *sc)
267199481Srdivacky{
268198090Srdivacky	return 0;
269198090Srdivacky}
270198090Srdivacky