+Cc: Jarkko Nikula <jarkko.nikula@xxxxxxxxxxxxxxx> > -----Original Message----- > From: Jiawen Wu <jiawenwu@xxxxxxxxxxxxxx> > Sent: Tuesday, April 11, 2023 5:27 PM > To: netdev@xxxxxxxxxxxxxxx; linux@xxxxxxxxxxxxxxx > Cc: linux-i2c@xxxxxxxxxxxxxxx; linux-gpio@xxxxxxxxxxxxxxx; > mengyuanlou@xxxxxxxxxxxxx; Jiawen Wu <jiawenwu@xxxxxxxxxxxxxx> > Subject: [PATCH net-next v2 2/6] net: txgbe: Implement I2C bus master > driver > > Implement I2C bus driver to send and receive I2C messages. > > This I2C license the IP of Synopsys Designware, but without interrupt > support on the hardware design. It seems that polling mode needs to be > added in Synopsys Designware I2C driver. But currently it can only be > driven by this I2C bus master driver. > > Signed-off-by: Jiawen Wu <jiawenwu@xxxxxxxxxxxxxx> > --- > drivers/net/ethernet/wangxun/Kconfig | 1 + > .../net/ethernet/wangxun/txgbe/txgbe_phy.c | 153 > ++++++++++++++++++ > .../net/ethernet/wangxun/txgbe/txgbe_type.h | 23 +++ > 3 files changed, 177 insertions(+) > > diff --git a/drivers/net/ethernet/wangxun/Kconfig > b/drivers/net/ethernet/wangxun/Kconfig > index c9d88673d306..8cbf0dd48a2c 100644 > --- a/drivers/net/ethernet/wangxun/Kconfig > +++ b/drivers/net/ethernet/wangxun/Kconfig > @@ -41,6 +41,7 @@ config TXGBE > tristate "Wangxun(R) 10GbE PCI Express adapters support" > depends on PCI > select LIBWX > + select I2C > help > This driver supports Wangxun(R) 10GbE PCI Express family of > adapters. > diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c > b/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c > index 86d5e0647d5e..2721da1625e0 100644 > --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c > +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c > @@ -2,9 +2,12 @@ > /* Copyright (c) 2015 - 2023 Beijing WangXun Technology Co., Ltd. */ > > #include <linux/gpio/property.h> > +#include <linux/iopoll.h> > +#include <linux/i2c.h> > #include <linux/pci.h> > > #include "../libwx/wx_type.h" > +#include "../libwx/wx_hw.h" > #include "txgbe_type.h" > #include "txgbe_phy.h" > > @@ -67,6 +70,142 @@ static int txgbe_swnodes_register(struct txgbe > *txgbe) > return software_node_register_node_group(nodes->group); > } > > +static void txgbe_i2c_start(struct wx *wx, u16 dev_addr) > +{ > + wr32(wx, TXGBE_I2C_ENABLE, 0); > + > + wr32(wx, TXGBE_I2C_CON, > + (TXGBE_I2C_CON_MASTER_MODE | > + TXGBE_I2C_CON_SPEED(1) | > + TXGBE_I2C_CON_RESTART_EN | > + TXGBE_I2C_CON_SLAVE_DISABLE)); > + wr32(wx, TXGBE_I2C_TAR, dev_addr); > + wr32(wx, TXGBE_I2C_SS_SCL_HCNT, 600); > + wr32(wx, TXGBE_I2C_SS_SCL_LCNT, 600); > + wr32(wx, TXGBE_I2C_RX_TL, 0); /* 1byte for rx full signal */ > + wr32(wx, TXGBE_I2C_TX_TL, 4); > + wr32(wx, TXGBE_I2C_SCL_STUCK_TIMEOUT, 0xFFFFFF); > + wr32(wx, TXGBE_I2C_SDA_STUCK_TIMEOUT, 0xFFFFFF); > + > + wr32(wx, TXGBE_I2C_INTR_MASK, 0); > + wr32(wx, TXGBE_I2C_ENABLE, 1); > +} > + > +static int txgbe_i2c_poll_intr(struct wx *wx, u16 intr) > +{ > + u16 val; > + > + return read_poll_timeout(rd32, val, (val & intr) == intr, > + 100, 1000, false, wx, > + TXGBE_I2C_RAW_INTR_STAT); > +} > + > +static int txgbe_read_i2c_bytes(struct wx *wx, u8 dev_addr, struct > i2c_msg *msg) > +{ > + int err, i; > + > + txgbe_i2c_start(wx, msg->addr); > + > + for (i = 0; i < msg->len; i++) { > + /* wait tx empty */ > + err = txgbe_i2c_poll_intr(wx, TXGBE_I2C_INTR_STAT_TEMP); > + if (err) > + return err; > + > + /* read data */ > + wr32(wx, TXGBE_I2C_DATA_CMD, > + (dev_addr + i) | TXGBE_I2C_DATA_CMD_STOP); > + wr32(wx, TXGBE_I2C_DATA_CMD, > + TXGBE_I2C_DATA_CMD_READ | > TXGBE_I2C_DATA_CMD_STOP); > + > + /* wait for read complete */ > + err = txgbe_i2c_poll_intr(wx, TXGBE_I2C_INTR_STAT_RFUL); > + if (err) > + return err; > + > + msg->buf[i] = 0xFF & rd32(wx, TXGBE_I2C_DATA_CMD); > + } > + > + return 0; > +} > + > +static int txgbe_write_i2c_bytes(struct wx *wx, struct i2c_msg *msg) > +{ > + int err, i; > + > + txgbe_i2c_start(wx, msg->addr); > + > + for (i = 0; i < msg->len; i++) { > + /* wait tx empty */ > + err = txgbe_i2c_poll_intr(wx, TXGBE_I2C_INTR_STAT_TEMP); > + if (err) > + return err; > + > + /* write data */ > + wr32(wx, TXGBE_I2C_DATA_CMD, msg->buf[i]); > + if (i == (msg->len - 1)) > + wr32(wx, TXGBE_I2C_DATA_CMD, > TXGBE_I2C_DATA_CMD_STOP); > + } > + > + return 0; > +} > + > +static int txgbe_i2c_xfer(struct i2c_adapter *i2c_adap, > + struct i2c_msg *msg, int num_msgs) > +{ > + struct wx *wx = i2c_get_adapdata(i2c_adap); > + u8 dev_addr = msg[0].buf[0]; > + int i, ret; > + > + for (i = 0; i < num_msgs; i++) { > + if (msg[i].flags & I2C_M_RD) > + ret = txgbe_read_i2c_bytes(wx, dev_addr, &msg[i]); > + else > + ret = txgbe_write_i2c_bytes(wx, &msg[i]); > + > + if (ret) > + return ret; > + } > + > + return num_msgs; > +} > + > +static u32 txgbe_i2c_func(struct i2c_adapter *adap) > +{ > + return I2C_FUNC_I2C; > +} > + > +static const struct i2c_algorithm txgbe_i2c_algo = { > + .master_xfer = txgbe_i2c_xfer, > + .functionality = txgbe_i2c_func, > +}; > + > +static int txgbe_i2c_adapter_add(struct txgbe *txgbe) > +{ > + struct pci_dev *pdev = txgbe->wx->pdev; > + struct i2c_adapter *i2c_adap; > + int ret; > + > + i2c_adap = devm_kzalloc(&pdev->dev, sizeof(*i2c_adap), > GFP_KERNEL); > + if (!i2c_adap) > + return -ENOMEM; > + > + i2c_adap->owner = THIS_MODULE; > + i2c_adap->algo = &txgbe_i2c_algo; > + i2c_adap->dev.parent = &pdev->dev; > + i2c_adap->dev.fwnode = > software_node_fwnode(txgbe->nodes.group[SWNODE_I2C]); > + strscpy(i2c_adap->name, "txgbe_i2c", sizeof(i2c_adap->name)); > + > + i2c_set_adapdata(i2c_adap, txgbe->wx); > + ret = i2c_add_adapter(i2c_adap); > + if (ret) > + return ret; > + > + txgbe->i2c_adap = i2c_adap; > + > + return 0; > +} > + > int txgbe_init_phy(struct txgbe *txgbe) > { > int ret; > @@ -77,10 +216,24 @@ int txgbe_init_phy(struct txgbe *txgbe) > return ret; > } > > + ret = txgbe_i2c_adapter_add(txgbe); > + if (ret) { > + wx_err(txgbe->wx, "failed to init i2c interface: %d\n", ret); > + goto err; > + } > + > return 0; > + > +err: > + txgbe_remove_phy(txgbe); > + > + return ret; > } > > void txgbe_remove_phy(struct txgbe *txgbe) > { > + if (txgbe->i2c_adap) > + i2c_del_adapter(txgbe->i2c_adap); > + > software_node_unregister_node_group(txgbe->nodes.group); > } > diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h > b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h > index d30684378f4e..6c02af196157 100644 > --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h > +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h > @@ -55,6 +55,28 @@ > #define TXGBE_TS_CTL 0x10300 > #define TXGBE_TS_CTL_EVAL_MD BIT(31) > > +/* I2C registers */ > +#define TXGBE_I2C_CON 0x14900 /* I2C > Control */ > +#define TXGBE_I2C_CON_SLAVE_DISABLE BIT(6) > +#define TXGBE_I2C_CON_RESTART_EN BIT(5) > +#define TXGBE_I2C_CON_SPEED(_v) > FIELD_PREP(GENMASK(2, 1), _v) > +#define TXGBE_I2C_CON_MASTER_MODE BIT(0) > +#define TXGBE_I2C_TAR 0x14904 /* I2C > Target Address */ > +#define TXGBE_I2C_DATA_CMD 0x14910 /* > I2C Rx/Tx Data Buf and Cmd */ > +#define TXGBE_I2C_DATA_CMD_STOP BIT(9) > +#define TXGBE_I2C_DATA_CMD_READ BIT(8) > +#define TXGBE_I2C_SS_SCL_HCNT 0x14914 > +#define TXGBE_I2C_SS_SCL_LCNT 0x14918 > +#define TXGBE_I2C_INTR_MASK 0x14930 /* I2C > Interrupt Mask */ > +#define TXGBE_I2C_RAW_INTR_STAT 0x14934 /* > I2C Raw Interrupt Status */ > +#define TXGBE_I2C_INTR_STAT_RFUL BIT(2) > +#define TXGBE_I2C_INTR_STAT_TEMP BIT(4) > +#define TXGBE_I2C_RX_TL 0x14938 /* I2C > Receive FIFO Threshold */ > +#define TXGBE_I2C_TX_TL 0x1493C /* I2C > TX FIFO Threshold */ > +#define TXGBE_I2C_ENABLE 0x1496C /* I2C > Enable */ > +#define TXGBE_I2C_SCL_STUCK_TIMEOUT 0x149AC > +#define TXGBE_I2C_SDA_STUCK_TIMEOUT 0x149B0 > + > /* Part Number String Length */ > #define TXGBE_PBANUM_LENGTH 32 > > @@ -139,6 +161,7 @@ struct txgbe_nodes { > struct txgbe { > struct wx *wx; > struct txgbe_nodes nodes; > + struct i2c_adapter *i2c_adap; > }; > > #endif /* _TXGBE_TYPE_H_ */ > -- > 2.27.0 >