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