diff --git a/sys/dev/cxgbe/common/common.h b/sys/dev/cxgbe/common/common.h index f61a8d724411..54647eaeaf17 100644 --- a/sys/dev/cxgbe/common/common.h +++ b/sys/dev/cxgbe/common/common.h @@ -521,6 +521,8 @@ int t4_enable_vi(struct adapter *adap, unsigned int mbox, unsigned int viid, bool rx_en, bool tx_en); int t4_identify_port(struct adapter *adap, unsigned int mbox, unsigned int viid, unsigned int nblinks); +int t4_i2c_rd(struct adapter *adap, unsigned int mbox, unsigned int port_id, + u8 dev_addr, u8 offset, u8 *valp); int t4_mdio_rd(struct adapter *adap, unsigned int mbox, unsigned int phy_addr, unsigned int mmd, unsigned int reg, unsigned int *valp); int t4_mdio_wr(struct adapter *adap, unsigned int mbox, unsigned int phy_addr, diff --git a/sys/dev/cxgbe/common/t4_hw.c b/sys/dev/cxgbe/common/t4_hw.c index 8cdcfe30d877..902a4e85c6be 100644 --- a/sys/dev/cxgbe/common/t4_hw.c +++ b/sys/dev/cxgbe/common/t4_hw.c @@ -3884,6 +3884,36 @@ int t4_fwaddrspace_write(struct adapter *adap, unsigned int mbox, u32 addr, u32 return t4_wr_mbox(adap, mbox, &c, sizeof(c), NULL); } +/** + * t4_i2c_rd - read a byte from an i2c addressable device + * @adap: the adapter + * @mbox: mailbox to use for the FW command + * @port_id: the port id + * @dev_addr: the i2c device address + * @offset: the byte offset to read from + * @valp: where to store the value + */ +int t4_i2c_rd(struct adapter *adap, unsigned int mbox, unsigned int port_id, + u8 dev_addr, u8 offset, u8 *valp) +{ + int ret; + struct fw_ldst_cmd c; + + memset(&c, 0, sizeof(c)); + c.op_to_addrspace = htonl(V_FW_CMD_OP(FW_LDST_CMD) | F_FW_CMD_REQUEST | + F_FW_CMD_READ | + V_FW_LDST_CMD_ADDRSPACE(FW_LDST_ADDRSPC_FUNC_I2C)); + c.cycles_to_len16 = htonl(FW_LEN16(c)); + c.u.i2c.pid_pkd = V_FW_LDST_CMD_PID(port_id); + c.u.i2c.base = dev_addr; + c.u.i2c.boffset = offset; + + ret = t4_wr_mbox(adap, mbox, &c, sizeof(c), &c); + if (ret == 0) + *valp = c.u.i2c.data; + return ret; +} + /** * t4_mdio_rd - read a PHY register through MDIO * @adap: the adapter diff --git a/sys/dev/cxgbe/t4_ioctl.h b/sys/dev/cxgbe/t4_ioctl.h index 2a3fa3998ef1..45c2fccb48c2 100644 --- a/sys/dev/cxgbe/t4_ioctl.h +++ b/sys/dev/cxgbe/t4_ioctl.h @@ -49,6 +49,7 @@ enum { T4_GET_SGE_CONTEXT, /* get SGE context for a queue */ T4_LOAD_FW, /* flash firmware */ T4_GET_MEM, /* read memory */ + T4_GET_I2C, /* read from i2c addressible device */ }; struct t4_reg { @@ -69,6 +70,14 @@ struct t4_data { uint8_t *data; }; +struct t4_i2c_data { + uint8_t port_id; + uint8_t dev_addr; + uint8_t offset; + uint8_t len; + uint8_t data[8]; +}; + /* * A hardware filter is some valid combination of these. */ @@ -224,4 +233,5 @@ struct t4_mem_range { struct t4_sge_context) #define CHELSIO_T4_LOAD_FW _IOW('f', T4_LOAD_FW, struct t4_data) #define CHELSIO_T4_GET_MEM _IOW('f', T4_GET_MEM, struct t4_mem_range) +#define CHELSIO_T4_GET_I2C _IOWR('f', T4_GET_I2C, struct t4_i2c_data) #endif diff --git a/sys/dev/cxgbe/t4_main.c b/sys/dev/cxgbe/t4_main.c index 50a8725ff03e..0097b44cc769 100644 --- a/sys/dev/cxgbe/t4_main.c +++ b/sys/dev/cxgbe/t4_main.c @@ -349,6 +349,7 @@ static int set_filter_wr(struct adapter *, int); static int del_filter_wr(struct adapter *, int); static int get_sge_context(struct adapter *, struct t4_sge_context *); static int read_card_mem(struct adapter *, struct t4_mem_range *); +static int read_i2c(struct adapter *, struct t4_i2c_data *); #ifdef TCP_OFFLOAD static int toe_capability(struct port_info *, int); #endif @@ -5170,6 +5171,27 @@ proceed: return (rc); } +static int +read_i2c(struct adapter *sc, struct t4_i2c_data *i2cd) +{ + int rc; + + ADAPTER_LOCK_ASSERT_OWNED(sc); /* for mbox */ + + if (i2cd->len == 0 || i2cd->port_id >= sc->params.nports) + return (EINVAL); + + if (i2cd->len > 1) { + /* XXX: need fw support for longer reads in one go */ + return (ENOTSUP); + } + + rc = -t4_i2c_rd(sc, sc->mbox, i2cd->port_id, i2cd->dev_addr, + i2cd->offset, &i2cd->data[0]); + + return (rc); +} + int t4_os_find_pci_capability(struct adapter *sc, int cap) { @@ -5373,6 +5395,11 @@ t4_ioctl(struct cdev *dev, unsigned long cmd, caddr_t data, int fflag, case CHELSIO_T4_GET_MEM: rc = read_card_mem(sc, (struct t4_mem_range *)data); break; + case CHELSIO_T4_GET_I2C: + ADAPTER_LOCK(sc); + rc = read_i2c(sc, (struct t4_i2c_data *)data); + ADAPTER_UNLOCK(sc); + break; default: rc = EINVAL; }