Message ID | 20230422045621.360918-3-jiawenwu@trustnetic.com (mailing list archive) |
---|---|
State | Superseded |
Delegated to: | Netdev Maintainers |
Headers | show |
Series | TXGBE PHYLINK support | expand |
On Sat, Apr 22, 2023 at 12:56:15PM +0800, 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 since resource > address was mapped on ethernet driver. Since there is no device tree to > get the clock, the parameters hcnt/lcnt are also set by platform data. > > The exists IP limitations are dealt as workarounds: > - IP does not support interrupt mode, it works on polling mode. > - Additionally set FIFO depth address the chip issue. > > Cc: Jarkko Nikula <jarkko.nikula@linux.intel.com> Please, use --cc parameter to `git format-patch ...`. Also for tag block we do not use blank lines. ... > #define MODEL_MSCC_OCELOT BIT(8) > #define MODEL_BAIKAL_BT1 BIT(9) > #define MODEL_AMD_NAVI_GPU BIT(10) > +#define MODEL_WANGXUN_SP BIT(11) > #define MODEL_MASK GENMASK(11, 8) Yeah, maybe next one will need to transform this from bitfield to plain number. ... > -static int amd_i2c_adap_quirk(struct dw_i2c_dev *dev) > +static int poll_i2c_adap_quirk(struct dw_i2c_dev *dev) i2c_dw_poll_adap_quirk() ... > +static bool i2c_is_model_poll(struct dw_i2c_dev *dev) i2c_dw_is_... ... > +++ b/include/linux/platform_data/i2c-dw.h No way we need this in a new code. > +struct dw_i2c_platform_data { > + void __iomem *base; You should use regmap. > + unsigned int flags; > + unsigned int ss_hcnt; > + unsigned int ss_lcnt; > + unsigned int fs_hcnt; > + unsigned int fs_lcnt; No, use device properties. > +};
> > +++ b/include/linux/platform_data/i2c-dw.h > > No way we need this in a new code. Do I have to rely on OF or ACPI if I need these parameters? > > > +struct dw_i2c_platform_data { > > + void __iomem *base; > > You should use regmap. The resource was mapped on the ethernet driver. How do I map it again with I2C offset? > > > + unsigned int flags; > > + unsigned int ss_hcnt; > > + unsigned int ss_lcnt; > > + unsigned int fs_hcnt; > > + unsigned int fs_lcnt; > > No, use device properties. > > > +}; > > -- > With Best Regards, > Andy Shevchenko > > >
> > +++ b/include/linux/platform_data/i2c-dw.h > > No way we need this in a new code. > > > +struct dw_i2c_platform_data { > > + void __iomem *base; > > You should use regmap. > > > + unsigned int flags; > > + unsigned int ss_hcnt; > > + unsigned int ss_lcnt; > > + unsigned int fs_hcnt; > > + unsigned int fs_lcnt; > > No, use device properties. > > > +}; > > -- > With Best Regards, > Andy Shevchenko > Is it acceptable to add such a function into dw_i2c_plat_probe()? Otherwise I really can't find a way to get these parameters without DT and ACPI. +static void i2c_dw_parse_property(struct dw_i2c_dev *dev) +{ + if (!is_software_node(dev_fwnode(dev->dev))) + return; + + if (!dev->flags) + device_property_read_u32(dev->dev, "dw-i2c-flags", &dev->flags); + + device_property_read_u16(dev->dev, "i2c-ss-scl-hcnt", &dev->ss_hcnt); + device_property_read_u16(dev->dev, "i2c-ss-scl-lcnt", &dev->ss_lcnt); + device_property_read_u16(dev->dev, "i2c-fs-scl-hcnt", &dev->fs_hcnt); + device_property_read_u16(dev->dev, "i2c-fs-scl-lcnt", &dev->fs_lcnt); + + if (!dev->ss_hcnt || !dev->ss_lcnt) { + dev->ss_hcnt = 6; + dev->ss_lcnt = 8; + } + if (!dev->fs_hcnt || !dev->fs_lcnt) { + dev->fs_hcnt = 6; + dev->fs_lcnt = 8; + } +}
On Sun, Apr 23, 2023 at 10:31:09AM +0800, Jiawen Wu wrote: > > > +++ b/include/linux/platform_data/i2c-dw.h > > > > No way we need this in a new code. > > Do I have to rely on OF or ACPI if I need these parameters? > > > > > > +struct dw_i2c_platform_data { > > > + void __iomem *base; > > > > You should use regmap. > > The resource was mapped on the ethernet driver. How do I map it again > with I2C offset? Create a regmap MMIO and pass the pointer to the child driver via existing private members. See how MFD drivers do that, e.g. intel_soc_pmic_*.c. > > > + unsigned int flags; > > > + unsigned int ss_hcnt; > > > + unsigned int ss_lcnt; > > > + unsigned int fs_hcnt; > > > + unsigned int fs_lcnt; > > > > No, use device properties. > > > > > +};
Hi Andy, [...] > > #define MODEL_MSCC_OCELOT BIT(8) > > #define MODEL_BAIKAL_BT1 BIT(9) > > #define MODEL_AMD_NAVI_GPU BIT(10) > > +#define MODEL_WANGXUN_SP BIT(11) > > #define MODEL_MASK GENMASK(11, 8) > > Yeah, maybe next one will need to transform this from bitfield to plain number. You mean this? -#define ACCESS_INTR_MASK BIT(0) -#define ACCESS_NO_IRQ_SUSPEND BIT(1) -#define ARBITRATION_SEMAPHORE BIT(2) - -#define MODEL_MSCC_OCELOT BIT(8) -#define MODEL_BAIKAL_BT1 BIT(9) -#define MODEL_AMD_NAVI_GPU BIT(10) -#define MODEL_MASK GENMASK(11, 8) +#define ACCESS_INTR_MASK 0x00 +#define ACCESS_NO_IRQ_SUSPEND 0x01 +#define ARBITRATION_SEMAPHORE 0x02 + +#define MODEL_MSCC_OCELOT 0x08 +#define MODEL_BAIKAL_BT1 0x09 +#define MODEL_AMD_NAVI_GPU 0x0a +#define MODEL_MASK 0x78 I actually like more bitfield to plain numbers. Andi
On Tue, Apr 25, 2023 at 04:08:59PM +0200, Andi Shyti wrote: > > > #define MODEL_MSCC_OCELOT BIT(8) > > > #define MODEL_BAIKAL_BT1 BIT(9) > > > #define MODEL_AMD_NAVI_GPU BIT(10) > > > +#define MODEL_WANGXUN_SP BIT(11) > > > #define MODEL_MASK GENMASK(11, 8) > > > > Yeah, maybe next one will need to transform this from bitfield to plain number. > > You mean this? No, only MODEL_XXX bits. > -#define ACCESS_INTR_MASK BIT(0) > -#define ACCESS_NO_IRQ_SUSPEND BIT(1) > -#define ARBITRATION_SEMAPHORE BIT(2) > - > -#define MODEL_MSCC_OCELOT BIT(8) > -#define MODEL_BAIKAL_BT1 BIT(9) > -#define MODEL_AMD_NAVI_GPU BIT(10) > -#define MODEL_MASK GENMASK(11, 8) > +#define ACCESS_INTR_MASK 0x00 > +#define ACCESS_NO_IRQ_SUSPEND 0x01 > +#define ARBITRATION_SEMAPHORE 0x02 > + > +#define MODEL_MSCC_OCELOT 0x08 > +#define MODEL_BAIKAL_BT1 0x09 > +#define MODEL_AMD_NAVI_GPU 0x0a > +#define MODEL_MASK 0x78 > > I actually like more bitfield to plain numbers. Too limited. For model we get 16 out of 4 bits, which is much better and as you see we have a trend.
diff --git a/drivers/i2c/busses/i2c-designware-common.c b/drivers/i2c/busses/i2c-designware-common.c index 0dc6b1ce663f..a7c2e67ccbf6 100644 --- a/drivers/i2c/busses/i2c-designware-common.c +++ b/drivers/i2c/busses/i2c-designware-common.c @@ -575,6 +575,14 @@ int i2c_dw_set_fifo_size(struct dw_i2c_dev *dev) unsigned int param; int ret; + /* DW_IC_COMP_PARAM_1 not implement for IP issue */ + if ((dev->flags & MODEL_MASK) == MODEL_WANGXUN_SP) { + dev->tx_fifo_depth = 4; + dev->rx_fifo_depth = 0; + + return 0; + } + /* * Try to detect the FIFO depth if not set by interface driver, * the depth could be from 2 to 256 from HW spec. diff --git a/drivers/i2c/busses/i2c-designware-core.h b/drivers/i2c/busses/i2c-designware-core.h index 050d8c63ad3c..c686198e12d2 100644 --- a/drivers/i2c/busses/i2c-designware-core.h +++ b/drivers/i2c/busses/i2c-designware-core.h @@ -303,6 +303,7 @@ struct dw_i2c_dev { #define MODEL_MSCC_OCELOT BIT(8) #define MODEL_BAIKAL_BT1 BIT(9) #define MODEL_AMD_NAVI_GPU BIT(10) +#define MODEL_WANGXUN_SP BIT(11) #define MODEL_MASK GENMASK(11, 8) /* diff --git a/drivers/i2c/busses/i2c-designware-master.c b/drivers/i2c/busses/i2c-designware-master.c index 55ea91a63382..962b791dae39 100644 --- a/drivers/i2c/busses/i2c-designware-master.c +++ b/drivers/i2c/busses/i2c-designware-master.c @@ -354,6 +354,68 @@ static int amd_i2c_dw_xfer_quirk(struct i2c_adapter *adap, struct i2c_msg *msgs, return 0; } +static int i2c_dw_poll_tx_empty(struct dw_i2c_dev *dev) +{ + u32 val; + + return regmap_read_poll_timeout(dev->map, DW_IC_RAW_INTR_STAT, val, + val & DW_IC_INTR_TX_EMPTY, + 100, 1000); +} + +static int i2c_dw_poll_rx_full(struct dw_i2c_dev *dev) +{ + u32 val; + + return regmap_read_poll_timeout(dev->map, DW_IC_RAW_INTR_STAT, val, + val & DW_IC_INTR_RX_FULL, + 100, 1000); +} + +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, stop = 0; + u8 *buf; + + dev->msgs = msgs; + dev->msgs_num = num_msgs; + i2c_dw_xfer_init(dev); + regmap_write(dev->map, DW_IC_INTR_MASK, 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 (msg_idx == num_msgs - 1 && data_idx == buf_len - 1) + stop |= BIT(9); + + if (msgs[msg_idx].flags & I2C_M_RD) { + regmap_write(dev->map, DW_IC_DATA_CMD, 0x100 | stop); + + ret = i2c_dw_poll_rx_full(dev); + if (ret) + return ret; + + regmap_read(dev->map, DW_IC_DATA_CMD, &val); + buf[data_idx] = val; + } else { + ret = i2c_dw_poll_tx_empty(dev); + if (ret) + return ret; + + regmap_write(dev->map, DW_IC_DATA_CMD, + buf[data_idx] | stop); + } + } + } + + return num_msgs; +} + /* * Initiate (and continue) low level master read/write transaction. * This function is only called from i2c_dw_isr, and pumping i2c_msg @@ -568,6 +630,11 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) goto done_nolock; } + if ((dev->flags & MODEL_MASK) == MODEL_WANGXUN_SP) { + ret = txgbe_i2c_dw_xfer_quirk(adap, msgs, num); + goto done_nolock; + } + reinit_completion(&dev->cmd_complete); dev->msgs = msgs; dev->msgs_num = num; @@ -848,7 +915,7 @@ static int i2c_dw_init_recovery_info(struct dw_i2c_dev *dev) return 0; } -static int amd_i2c_adap_quirk(struct dw_i2c_dev *dev) +static int poll_i2c_adap_quirk(struct dw_i2c_dev *dev) { struct i2c_adapter *adap = &dev->adapter; int ret; @@ -862,6 +929,17 @@ static int amd_i2c_adap_quirk(struct dw_i2c_dev *dev) return ret; } +static bool i2c_is_model_poll(struct dw_i2c_dev *dev) +{ + switch (dev->flags & MODEL_MASK) { + case MODEL_AMD_NAVI_GPU: + case MODEL_WANGXUN_SP: + return true; + default: + return false; + } +} + int i2c_dw_probe_master(struct dw_i2c_dev *dev) { struct i2c_adapter *adap = &dev->adapter; @@ -917,8 +995,8 @@ int i2c_dw_probe_master(struct dw_i2c_dev *dev) adap->dev.parent = dev->dev; i2c_set_adapdata(adap, dev); - if ((dev->flags & MODEL_MASK) == MODEL_AMD_NAVI_GPU) - return amd_i2c_adap_quirk(dev); + if (i2c_is_model_poll(dev)) + return poll_i2c_adap_quirk(dev); if (dev->flags & ACCESS_NO_IRQ_SUSPEND) { irq_flags = IRQF_NO_SUSPEND; diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c index 74182db03a88..10b2c61b279f 100644 --- a/drivers/i2c/busses/i2c-designware-platdrv.c +++ b/drivers/i2c/busses/i2c-designware-platdrv.c @@ -23,6 +23,7 @@ #include <linux/module.h> #include <linux/of.h> #include <linux/platform_device.h> +#include <linux/platform_data/i2c-dw.h> #include <linux/pm.h> #include <linux/pm_runtime.h> #include <linux/property.h> @@ -179,12 +180,14 @@ static void dw_i2c_plat_pm_cleanup(struct dw_i2c_dev *dev) static int dw_i2c_plat_request_regs(struct dw_i2c_dev *dev) { struct platform_device *pdev = to_platform_device(dev->dev); - int ret; + int ret = 0; switch (dev->flags & MODEL_MASK) { case MODEL_BAIKAL_BT1: ret = bt1_i2c_request_regs(dev); break; + case MODEL_WANGXUN_SP: + break; default: dev->base = devm_platform_ioremap_resource(pdev, 0); ret = PTR_ERR_OR_ZERO(dev->base); @@ -194,6 +197,35 @@ static int dw_i2c_plat_request_regs(struct dw_i2c_dev *dev) return ret; } +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); + ret = dw_i2c_plat_request_regs(dev); if (ret) return ret; diff --git a/include/linux/platform_data/i2c-dw.h b/include/linux/platform_data/i2c-dw.h new file mode 100644 index 000000000000..f4552df08084 --- /dev/null +++ b/include/linux/platform_data/i2c-dw.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef _LINUX_I2C_DW_H +#define _LINUX_I2C_DW_H + +struct dw_i2c_platform_data { + void __iomem *base; + unsigned int flags; + unsigned int ss_hcnt; + unsigned int ss_lcnt; + unsigned int fs_hcnt; + unsigned int fs_lcnt; +}; + +#endif /* _LINUX_I2C_DW_H */
Wangxun 10Gb ethernet chip is connected to Designware I2C, to communicate with SFP. Add platform data to pass IOMEM base address, board flag since resource address was mapped on ethernet driver. Since there is no device tree to get the clock, the parameters hcnt/lcnt are also set by platform data. The exists IP limitations are dealt as workarounds: - IP does not support interrupt mode, it works on polling mode. - 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 | 8 ++ drivers/i2c/busses/i2c-designware-core.h | 1 + drivers/i2c/busses/i2c-designware-master.c | 84 ++++++++++++++++++++- drivers/i2c/busses/i2c-designware-platdrv.c | 36 ++++++++- include/linux/platform_data/i2c-dw.h | 15 ++++ 5 files changed, 140 insertions(+), 4 deletions(-) create mode 100644 include/linux/platform_data/i2c-dw.h