Re: [PATCH net-next v3 2/8] i2c: designware: Add driver support for Wangxun 10Gb NIC

Jarkko Nikula
2023-04-19T14:36:54Z

Hi

On 4/19/23 11:27, Jiawen Wu wrote:
> Wangxun 10Gb ethernet chip is connected to Designware I2C, to communicate
> with SFP.
>
> Add platform data to pass IOMEM base address, board flag and other
> parameters, since resource address was mapped on ethernet driver.
>
> The exists IP limitations are dealt as workarounds:
> - IP does not support interrupt mode, it works on polling mode.
> - I2C cannot read continuously, only one byte can at a time.
> - Additionally set FIFO depth address the chip issue.
>
> Cc: Jarkko Nikula <jarkko.nikula@linux.intel.com>
>
> Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com>
> ---
> drivers/i2c/busses/i2c-designware-common.c | 4 +
> drivers/i2c/busses/i2c-designware-core.h | 1 +
> drivers/i2c/busses/i2c-designware-master.c | 91 ++++++++++++++++++++-
> drivers/i2c/busses/i2c-designware-platdrv.c | 36 +++++++-
> include/linux/platform_data/i2c-dw.h | 15 ++++
> 5 files changed, 143 insertions(+), 4 deletions(-)
> create mode 100644 include/linux/platform_data/i2c-dw.h
>
> diff --git a/drivers/i2c/busses/i2c-designware-common.c b/drivers/i2c/busses/i2c-designware-common.c
> index 0dc6b1ce663f..e4faab4655cb 100644
> --- a/drivers/i2c/busses/i2c-designware-common.c
> +++ b/drivers/i2c/busses/i2c-designware-common.c
> @@ -588,6 +588,10 @@ int i2c_dw_set_fifo_size(struct dw_i2c_dev *dev)
> if (ret)
> return ret;
>
> + /* workaround for IP hardware issue */
> + if ((dev->flags & MODEL_MASK) == MODEL_WANGXUN_SP)
> + param |= 0x80800;
> +
What is the issue here? Is register DW_IC_COMP_PARAM_1 implemented? If
not then I think safer is not to even read it lines before this added test.

> +static int txgbe_i2c_dw_xfer_quirk(struct i2c_adapter *adap, struct i2c_msg *msgs,
> + int num_msgs)
> +{
> + struct dw_i2c_dev *dev = i2c_get_adapdata(adap);
> + int msg_idx, buf_len, data_idx, ret;
> + unsigned int val;
> + u8 dev_addr;
> + u8 *buf;
> +
> + dev->msgs = msgs;
> + dev->msgs_num = num_msgs;
> + i2c_dw_xfer_init(dev);
> + regmap_write(dev->map, DW_IC_INTR_MASK, 0);
> +
> + dev_addr = msgs[0].buf[0];
> +
> + for (msg_idx = 0; msg_idx < num_msgs; msg_idx++) {
> + buf = msgs[msg_idx].buf;
> + buf_len = msgs[msg_idx].len;
> +
> + for (data_idx = 0; data_idx < buf_len; data_idx++) {
> + if (msgs[msg_idx].flags & I2C_M_RD) {
> + ret = i2c_dw_poll_tx_empty(dev);
> + if (ret)
> + return ret;
> +
> + regmap_write(dev->map, DW_IC_DATA_CMD,
> + (dev_addr + data_idx) | BIT(9));
> + regmap_write(dev->map, DW_IC_DATA_CMD, 0x100 | BIT(9));
> +

Am I wrong but this looks tailored to the use-case rather than generic
implementation? I don't understand what is this write command with data
(dev_addr + data_idx) + STOP followed by read command with STOP.

DW_IC_DATA_CMD upper bits have following meaning:

BIT(8) == 0x100: 1 = read, 0 = write
BIT(9): Stop issued after this byte
BIT(10): RESTART is issued before the byte is sent or received

> + } else {
> + ret = i2c_dw_poll_tx_empty(dev);
> + if (ret)
> + return ret;
> +
> + regmap_write(dev->map, DW_IC_DATA_CMD, buf[data_idx]);
> + if (data_idx == (buf_len - 1))
> + regmap_write(dev->map, DW_IC_DATA_CMD, BIT(9));

I think these separate writes must be combined if I'm not wrong? I
believe this cause needless extra zero byte + STOP transferred on the
bus instead of last buffer byte + STOP?

> --- a/drivers/i2c/busses/i2c-designware-platdrv.c
> +++ b/drivers/i2c/busses/i2c-designware-platdrv.c

> +static void dw_i2c_get_plat_data(struct dw_i2c_dev *dev)
> +{
> + struct platform_device *pdev = to_platform_device(dev->dev);
> + struct dw_i2c_platform_data *pdata;
> +
> + pdata = dev_get_platdata(&pdev->dev);
> + if (!pdata)
> + return;
> +
> + dev->flags |= pdata->flags;
> + dev->base = pdata->base;
> +
> + if (pdata->ss_hcnt && pdata->ss_lcnt) {
> + dev->ss_hcnt = pdata->ss_hcnt;
> + dev->ss_lcnt = pdata->ss_lcnt;
> + } else {
> + dev->ss_hcnt = 6;
> + dev->ss_lcnt = 8;
> + }
> +
> + if (pdata->fs_hcnt && pdata->fs_lcnt) {
> + dev->fs_hcnt = pdata->fs_hcnt;
> + dev->fs_lcnt = pdata->fs_lcnt;
> + } else {
> + dev->fs_hcnt = 6;
> + dev->fs_lcnt = 8;
> + }
> +}
> +
> static const struct dmi_system_id dw_i2c_hwmon_class_dmi[] = {
> {
> .ident = "Qtechnology QT5222",
> @@ -282,6 +314,8 @@ static int dw_i2c_plat_probe(struct platform_device *pdev)
> dev->irq = irq;
> platform_set_drvdata(pdev, dev);
>
> + dw_i2c_get_plat_data(dev);
> +
Instead of this added code would it be possible to use generic timing
parameters which can come either from firmware or code? Those are
handled already here by the call to i2c_parse_fw_timings().

Then drivers/i2c/busses/i2c-designware-master.c:
i2c_dw_set_timings_master() takes care of calculating Designware
specific hcnt/lcnt timing parameters from those generic values.