xref: /openbsd/sys/dev/i2c/ipmi_i2c.c (revision 3a3d566b)
1*3a3d566bSjsg /*	$OpenBSD: ipmi_i2c.c,v 1.6 2024/10/09 00:38:26 jsg Exp $	*/
219146c2bSkettenis /*
319146c2bSkettenis  * Copyright (c) 2019 Mark Kettenis <kettenis@openbsd.org>
419146c2bSkettenis  *
519146c2bSkettenis  * Permission to use, copy, modify, and distribute this software for any
619146c2bSkettenis  * purpose with or without fee is hereby granted, provided that the above
719146c2bSkettenis  * copyright notice and this permission notice appear in all copies.
819146c2bSkettenis  *
919146c2bSkettenis  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1019146c2bSkettenis  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1119146c2bSkettenis  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1219146c2bSkettenis  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1319146c2bSkettenis  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1419146c2bSkettenis  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1519146c2bSkettenis  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1619146c2bSkettenis  */
1719146c2bSkettenis 
1819146c2bSkettenis #include <sys/param.h>
1919146c2bSkettenis #include <sys/systm.h>
2019146c2bSkettenis #include <sys/device.h>
2119146c2bSkettenis 
2219146c2bSkettenis #include <machine/bus.h>
2319146c2bSkettenis 
2419146c2bSkettenis #include <dev/i2c/i2cvar.h>
2519146c2bSkettenis #include <dev/ipmivar.h>
2619146c2bSkettenis 
2719146c2bSkettenis #define BMC_SA			0x20	/* BMC/ESM3 */
2819146c2bSkettenis #define BMC_LUN			0
2919146c2bSkettenis 
3019146c2bSkettenis struct ipmi_i2c_softc {
3119146c2bSkettenis 	struct ipmi_softc sc;
3219146c2bSkettenis 	i2c_tag_t	sc_tag;
3319146c2bSkettenis 	i2c_addr_t	sc_addr;
3419146c2bSkettenis 	uint8_t		sc_rev;
3519146c2bSkettenis };
3619146c2bSkettenis 
3719146c2bSkettenis void	cmn_buildmsg(struct ipmi_cmd *);
3819146c2bSkettenis int	ssif_sendmsg(struct ipmi_cmd *);
3919146c2bSkettenis int	ssif_recvmsg(struct ipmi_cmd *);
4019146c2bSkettenis int	ssif_reset(struct ipmi_softc *);
4119146c2bSkettenis int	ssif_probe(struct ipmi_softc *);
4219146c2bSkettenis 
4319146c2bSkettenis struct ipmi_if ssif_if = {
4419146c2bSkettenis 	"SSIF",
4519146c2bSkettenis 	0,
4619146c2bSkettenis 	cmn_buildmsg,
4719146c2bSkettenis 	ssif_sendmsg,
4819146c2bSkettenis 	ssif_recvmsg,
4919146c2bSkettenis 	ssif_reset,
5019146c2bSkettenis 	ssif_probe,
5119146c2bSkettenis 	IPMI_MSG_DATASND,
5219146c2bSkettenis 	IPMI_MSG_DATARCV
5319146c2bSkettenis };
5419146c2bSkettenis 
5519146c2bSkettenis int	ipmi_i2c_match(struct device *, void *, void *);
5619146c2bSkettenis void	ipmi_i2c_attach(struct device *, struct device *, void *);
5719146c2bSkettenis 
58471aeecfSnaddy const struct cfattach ipmi_i2c_ca = {
59*3a3d566bSjsg 	sizeof(struct ipmi_i2c_softc), ipmi_i2c_match, ipmi_i2c_attach,
60*3a3d566bSjsg 	NULL, ipmi_activate
6119146c2bSkettenis };
6219146c2bSkettenis 
6319146c2bSkettenis int	ipmi_i2c_get_interface_caps(struct ipmi_i2c_softc *);
6419146c2bSkettenis int	ipmi_i2c_get_device_id(struct ipmi_i2c_softc *);
6519146c2bSkettenis 
6619146c2bSkettenis int
ipmi_i2c_match(struct device * parent,void * match,void * aux)6719146c2bSkettenis ipmi_i2c_match(struct device *parent, void *match, void *aux)
6819146c2bSkettenis {
6919146c2bSkettenis 	struct i2c_attach_args *ia = aux;
7019146c2bSkettenis 
7119146c2bSkettenis 	return (strcmp(ia->ia_name, "IPI0001") == 0 ||
7219146c2bSkettenis 	    strcmp(ia->ia_name, "APMC0D8A") == 0);
7319146c2bSkettenis }
7419146c2bSkettenis 
7519146c2bSkettenis void
ipmi_i2c_attach(struct device * parent,struct device * self,void * aux)7619146c2bSkettenis ipmi_i2c_attach(struct device *parent, struct device *self, void *aux)
7719146c2bSkettenis {
7819146c2bSkettenis 	struct ipmi_i2c_softc *sc = (struct ipmi_i2c_softc *)self;
7919146c2bSkettenis 	struct i2c_attach_args *ia = aux;
8019146c2bSkettenis 	struct ipmi_attach_args iaa;
8119146c2bSkettenis 
8219146c2bSkettenis 	sc->sc_tag = ia->ia_tag;
8319146c2bSkettenis 	sc->sc_addr = ia->ia_addr;
8419146c2bSkettenis 	sc->sc.sc_if = &ssif_if;
8519146c2bSkettenis 
8619146c2bSkettenis 	if (ipmi_i2c_get_interface_caps(sc)) {
8719146c2bSkettenis 		printf(": can't get system interface capabilities\n");
8819146c2bSkettenis 		return;
8919146c2bSkettenis 	}
9019146c2bSkettenis 
9119146c2bSkettenis 	if (ipmi_i2c_get_device_id(sc)) {
9219146c2bSkettenis 		printf(": can't get system interface capabilities\n");
9319146c2bSkettenis 		return;
9419146c2bSkettenis 	}
9519146c2bSkettenis 
9619146c2bSkettenis 	memset(&iaa, 0, sizeof(iaa));
9719146c2bSkettenis 	iaa.iaa_if_type = IPMI_IF_SSIF;
9819146c2bSkettenis 	iaa.iaa_if_rev = (sc->sc_rev >> 4 | sc->sc_rev << 4);
9919146c2bSkettenis 	iaa.iaa_if_irq = -1;
10019146c2bSkettenis 	ipmi_attach_common(&sc->sc, &iaa);
10119146c2bSkettenis }
10219146c2bSkettenis 
10319146c2bSkettenis int
ipmi_i2c_get_interface_caps(struct ipmi_i2c_softc * sc)10419146c2bSkettenis ipmi_i2c_get_interface_caps(struct ipmi_i2c_softc *sc)
10519146c2bSkettenis {
10619146c2bSkettenis 	struct ipmi_cmd c;
10719146c2bSkettenis 	uint8_t data[5];
10819146c2bSkettenis 
10919146c2bSkettenis 	data[0] = 0;		/* SSIF */
11019146c2bSkettenis 
11119146c2bSkettenis 	c.c_sc = &sc->sc;
11219146c2bSkettenis 	c.c_rssa = BMC_SA;
11319146c2bSkettenis 	c.c_rslun = BMC_LUN;
11419146c2bSkettenis 	c.c_netfn = APP_NETFN;
11519146c2bSkettenis 	c.c_cmd = APP_GET_SYSTEM_INTERFACE_CAPS;
11619146c2bSkettenis 	c.c_txlen = 1;
11719146c2bSkettenis 	c.c_rxlen = 0;
11819146c2bSkettenis 	c.c_maxrxlen = sizeof(data);
11919146c2bSkettenis 	c.c_data = data;
12019146c2bSkettenis 	if (ipmi_sendcmd(&c) || ipmi_recvcmd(&c))
12119146c2bSkettenis 		return EIO;
12219146c2bSkettenis 
12319146c2bSkettenis 	/* Check SSIF version number. */
12419146c2bSkettenis 	if ((data[1] & 0x7) != 0)
12519146c2bSkettenis 		return EINVAL;
12619146c2bSkettenis 	/* Check input and output message sizes. */
12719146c2bSkettenis 	if (data[2] < 32 || data[3] < 32)
12819146c2bSkettenis 		return EINVAL;
12919146c2bSkettenis 
13019146c2bSkettenis 	return 0;
13119146c2bSkettenis }
13219146c2bSkettenis 
13319146c2bSkettenis int
ipmi_i2c_get_device_id(struct ipmi_i2c_softc * sc)13419146c2bSkettenis ipmi_i2c_get_device_id(struct ipmi_i2c_softc *sc)
13519146c2bSkettenis {
13619146c2bSkettenis 	struct ipmi_cmd c;
13719146c2bSkettenis 	uint8_t data[16];
13819146c2bSkettenis 
13919146c2bSkettenis 	c.c_sc = &sc->sc;
14019146c2bSkettenis 	c.c_rssa = BMC_SA;
14119146c2bSkettenis 	c.c_rslun = BMC_LUN;
14219146c2bSkettenis 	c.c_netfn = APP_NETFN;
14319146c2bSkettenis 	c.c_cmd = APP_GET_DEVICE_ID;
14419146c2bSkettenis 	c.c_txlen = 0;
14519146c2bSkettenis 	c.c_rxlen = 0;
14619146c2bSkettenis 	c.c_maxrxlen = sizeof(data);
14719146c2bSkettenis 	c.c_data = data;
14819146c2bSkettenis 	if (ipmi_sendcmd(&c) || ipmi_recvcmd(&c))
14919146c2bSkettenis 		return EIO;
15019146c2bSkettenis 
15119146c2bSkettenis 	sc->sc_rev = data[4];
15219146c2bSkettenis 	return 0;
15319146c2bSkettenis }
15419146c2bSkettenis 
15519146c2bSkettenis int
ssif_sendmsg(struct ipmi_cmd * c)15619146c2bSkettenis ssif_sendmsg(struct ipmi_cmd *c)
15719146c2bSkettenis {
15819146c2bSkettenis 	struct ipmi_i2c_softc *sc = (struct ipmi_i2c_softc *)c->c_sc;
15919146c2bSkettenis 	uint8_t *buf = sc->sc.sc_buf;
16019146c2bSkettenis 	uint8_t cmd[2];
16119146c2bSkettenis 	int error, retry;
16219146c2bSkettenis 
16319146c2bSkettenis 	/* We only support single-part writes. */
16419146c2bSkettenis 	if (c->c_txlen > 32)
16519146c2bSkettenis 		return -1;
16619146c2bSkettenis 
16719146c2bSkettenis 	iic_acquire_bus(sc->sc_tag, 0);
16819146c2bSkettenis 
16919146c2bSkettenis 	cmd[0] = 2;
17019146c2bSkettenis 	cmd[1] = c->c_txlen;
1711f69a44eSkettenis 	for (retry = 0; retry < 5; retry++) {
17235d8351cSkettenis 		error = iic_exec(sc->sc_tag, I2C_OP_WRITE_BLOCK,
17319146c2bSkettenis 		    sc->sc_addr, cmd, sizeof(cmd), buf, c->c_txlen, 0);
17419146c2bSkettenis 		if (!error)
17519146c2bSkettenis 			break;
17619146c2bSkettenis 	}
17719146c2bSkettenis 
17819146c2bSkettenis 	iic_release_bus(sc->sc_tag, 0);
17919146c2bSkettenis 
18019146c2bSkettenis 	return (error ? -1 : 0);
18119146c2bSkettenis }
18219146c2bSkettenis 
18319146c2bSkettenis int
ssif_recvmsg(struct ipmi_cmd * c)18419146c2bSkettenis ssif_recvmsg(struct ipmi_cmd *c)
18519146c2bSkettenis {
18619146c2bSkettenis 	struct ipmi_i2c_softc *sc = (struct ipmi_i2c_softc *)c->c_sc;
18735d8351cSkettenis 	uint8_t buf[33];
18819146c2bSkettenis 	uint8_t cmd[1];
18935d8351cSkettenis 	uint8_t len;
19019146c2bSkettenis 	int error, retry;
19135d8351cSkettenis 	int blkno = 0;
19219146c2bSkettenis 
19319146c2bSkettenis 	iic_acquire_bus(sc->sc_tag, 0);
19419146c2bSkettenis 
19519146c2bSkettenis 	cmd[0] = 3;
19619146c2bSkettenis 	for (retry = 0; retry < 250; retry++) {
19735d8351cSkettenis 		memset(buf, 0, sizeof(buf));
19835d8351cSkettenis 		error = iic_exec(sc->sc_tag, I2C_OP_READ_BLOCK,
19935d8351cSkettenis 		    sc->sc_addr, cmd, sizeof(cmd), buf, sizeof(buf), 0);
20035d8351cSkettenis 		if (error)
20135d8351cSkettenis 			continue;
20235d8351cSkettenis 
20335d8351cSkettenis 		if (buf[0] < 1 || buf[0] > 32) {
20435d8351cSkettenis 			error = EIO;
20535d8351cSkettenis 			goto release;
20619146c2bSkettenis 		}
20719146c2bSkettenis 
20835d8351cSkettenis 		if (buf[0] == 32 && buf[1] == 0x00 && buf[2] == 0x01) {
20935d8351cSkettenis 			/* Multi-part read start. */
21035d8351cSkettenis 			c->c_rxlen = MIN(30, c->c_maxrxlen);
21135d8351cSkettenis 			memcpy(sc->sc.sc_buf, &buf[3], c->c_rxlen);
21235d8351cSkettenis 			break;
21335d8351cSkettenis 		} else {
21435d8351cSkettenis 			/* Single-part read. */
21535d8351cSkettenis 			c->c_rxlen = MIN(buf[0], c->c_maxrxlen);
21635d8351cSkettenis 			memcpy(sc->sc.sc_buf, &buf[1], c->c_rxlen);
21735d8351cSkettenis 			goto release;
21835d8351cSkettenis 		}
21935d8351cSkettenis 	}
22035d8351cSkettenis 	if (retry == 250)
22135d8351cSkettenis 		goto release;
22235d8351cSkettenis 
22335d8351cSkettenis 	cmd[0] = 9;
22435d8351cSkettenis 	while (buf[1] != 0xff && c->c_rxlen < c->c_maxrxlen) {
22535d8351cSkettenis 		memset(buf, 0, sizeof(buf));
22635d8351cSkettenis 		error = iic_exec(sc->sc_tag, I2C_OP_READ_BLOCK,
22735d8351cSkettenis 		    sc->sc_addr, cmd, sizeof(cmd), buf, sizeof(buf), 0);
22835d8351cSkettenis 		if (error)
22935d8351cSkettenis 			goto release;
23035d8351cSkettenis 
23135d8351cSkettenis 		if (buf[0] < 1 || buf[0] > 32) {
23235d8351cSkettenis 			error = EIO;
23335d8351cSkettenis 			goto release;
23435d8351cSkettenis 		}
23535d8351cSkettenis 
23635d8351cSkettenis 		if (buf[0] == 32 && buf[1] == blkno) {
23735d8351cSkettenis 			/* Multi-part read middle. */
23835d8351cSkettenis 			len = MIN(31, c->c_maxrxlen - c->c_rxlen);
23935d8351cSkettenis 			memcpy(&sc->sc.sc_buf[c->c_rxlen], &buf[2], len);
24035d8351cSkettenis 		} else if (buf[1] == 0xff) {
24135d8351cSkettenis 			/* Multi-part read end. */
24235d8351cSkettenis 			len = MIN(buf[0] - 1, c->c_maxrxlen - c->c_rxlen);
24335d8351cSkettenis 			memcpy(&sc->sc.sc_buf[c->c_rxlen], &buf[2], len);
24435d8351cSkettenis 		} else {
24535d8351cSkettenis 			error = EIO;
24635d8351cSkettenis 			goto release;
24735d8351cSkettenis 		}
24835d8351cSkettenis 		c->c_rxlen += len;
24935d8351cSkettenis 		blkno++;
25035d8351cSkettenis 	}
25135d8351cSkettenis 
25235d8351cSkettenis release:
25319146c2bSkettenis 	iic_release_bus(sc->sc_tag, 0);
25419146c2bSkettenis 
25519146c2bSkettenis 	return (error ? -1 : 0);
25619146c2bSkettenis }
25719146c2bSkettenis 
25819146c2bSkettenis int
ssif_reset(struct ipmi_softc * sc)25919146c2bSkettenis ssif_reset(struct ipmi_softc *sc)
26019146c2bSkettenis {
26119146c2bSkettenis 	return -1;
26219146c2bSkettenis }
26319146c2bSkettenis 
26419146c2bSkettenis int
ssif_probe(struct ipmi_softc * sc)26519146c2bSkettenis ssif_probe(struct ipmi_softc *sc)
26619146c2bSkettenis {
26719146c2bSkettenis 	return 0;
26819146c2bSkettenis }
269