From 5731cb4bef55d1fb649bc862b8e6f8e4cfd10e92 Mon Sep 17 00:00:00 2001 From: Patrick Rudolph Date: Tue, 30 Jan 2024 17:49:00 +0530 Subject: dt-bindings: i2c: pca954x: Add custom properties for MAX7357 Maxim Max7357 has a configuration register to enable additional features. These features aren't enabled by default & its up to board designer to enable the same as it may have unexpected side effects. These should be validated for proper functioning & detection of devices in secondary bus as sometimes it can cause secondary bus being disabled. Add booleans for: - maxim,isolate-stuck-channel - maxim,send-flush-out-sequence - maxim,preconnection-wiggle-test-enable Signed-off-by: Patrick Rudolph Signed-off-by: Naresh Solanki Reviewed-by: Rob Herring Signed-off-by: Andi Shyti --- .../devicetree/bindings/i2c/i2c-mux-pca954x.yaml | 30 ++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/Documentation/devicetree/bindings/i2c/i2c-mux-pca954x.yaml b/Documentation/devicetree/bindings/i2c/i2c-mux-pca954x.yaml index 2d7bb998b0e9..9aa0585200c9 100644 --- a/Documentation/devicetree/bindings/i2c/i2c-mux-pca954x.yaml +++ b/Documentation/devicetree/bindings/i2c/i2c-mux-pca954x.yaml @@ -71,6 +71,23 @@ properties: description: A voltage regulator supplying power to the chip. On PCA9846 the regulator supplies power to VDD2 (core logic) and optionally to VDD1. + maxim,isolate-stuck-channel: + type: boolean + description: Allows to use non faulty channels while a stuck channel is + isolated from the upstream bus. If not set all channels are isolated from + the upstream bus until the fault is cleared. + + maxim,send-flush-out-sequence: + type: boolean + description: Send a flush-out sequence to stuck auxiliary buses + automatically after a stuck channel is being detected. + + maxim,preconnection-wiggle-test-enable: + type: boolean + description: Send a STOP condition to the auxiliary buses when the switch + register activates a channel to detect a stuck high fault. On fault the + channel is isolated from the upstream bus. + required: - compatible - reg @@ -95,6 +112,19 @@ allOf: "#interrupt-cells": false interrupt-controller: false + - if: + not: + properties: + compatible: + contains: + enum: + - maxim,max7357 + then: + properties: + maxim,isolate-stuck-channel: false + maxim,send-flush-out-sequence: false + maxim,preconnection-wiggle-test-enable: false + unevaluatedProperties: false examples: -- cgit v1.2.3 From df7cbce47a993b14dc2cf71ba0cba081766277e0 Mon Sep 17 00:00:00 2001 From: Patrick Rudolph Date: Tue, 30 Jan 2024 17:49:01 +0530 Subject: i2c: muxes: pca954x: Enable features on MAX7357 Enable additional features based on DT settings and unconditionally release the shared interrupt pin after 1.6 seconds and allow to use it as reset. These features aren't enabled by default and it's up to board designer to validate for proper functioning and detection of devices in secondary bus as sometimes it can cause secondary bus being disabled. Signed-off-by: Patrick Rudolph Signed-off-by: Naresh Solanki Reviewed-by: Andi Shyti Signed-off-by: Andi Shyti --- drivers/i2c/muxes/i2c-mux-pca954x.c | 43 ++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/muxes/i2c-mux-pca954x.c b/drivers/i2c/muxes/i2c-mux-pca954x.c index 2219062104fb..f5dfc33b97c0 100644 --- a/drivers/i2c/muxes/i2c-mux-pca954x.c +++ b/drivers/i2c/muxes/i2c-mux-pca954x.c @@ -57,6 +57,20 @@ #define PCA954X_IRQ_OFFSET 4 +/* + * MAX7357's configuration register is writeable after POR, but + * can be locked by setting the basic mode bit. MAX7358 configuration + * register is locked by default and needs to be unlocked first. + * The configuration register holds the following settings: + */ +#define MAX7357_CONF_INT_ENABLE BIT(0) +#define MAX7357_CONF_FLUSH_OUT BIT(1) +#define MAX7357_CONF_RELEASE_INT BIT(2) +#define MAX7357_CONF_DISCON_SINGLE_CHAN BIT(4) +#define MAX7357_CONF_PRECONNECT_TEST BIT(7) + +#define MAX7357_POR_DEFAULT_CONF MAX7357_CONF_INT_ENABLE + enum pca_type { max_7356, max_7357, @@ -470,7 +484,34 @@ static int pca954x_init(struct i2c_client *client, struct pca954x *data) else data->last_chan = 0; /* Disconnect multiplexer */ - ret = i2c_smbus_write_byte(client, data->last_chan); + if (device_is_compatible(&client->dev, "maxim,max7357")) { + if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) { + u8 conf = MAX7357_POR_DEFAULT_CONF; + /* + * The interrupt signal is shared with the reset pin. Release the + * interrupt after 1.6 seconds to allow using the pin as reset. + */ + conf |= MAX7357_CONF_RELEASE_INT; + + if (device_property_read_bool(&client->dev, "maxim,isolate-stuck-channel")) + conf |= MAX7357_CONF_DISCON_SINGLE_CHAN; + if (device_property_read_bool(&client->dev, + "maxim,send-flush-out-sequence")) + conf |= MAX7357_CONF_FLUSH_OUT; + if (device_property_read_bool(&client->dev, + "maxim,preconnection-wiggle-test-enable")) + conf |= MAX7357_CONF_PRECONNECT_TEST; + + ret = i2c_smbus_write_byte_data(client, data->last_chan, conf); + } else { + dev_warn(&client->dev, "Write byte data not supported." + "Cannot enable enhanced mode features\n"); + ret = i2c_smbus_write_byte(client, data->last_chan); + } + } else { + ret = i2c_smbus_write_byte(client, data->last_chan); + } + if (ret < 0) data->last_chan = 0; -- cgit v1.2.3 From ecaaeff9006c0577e31d211066e193ce34ffba46 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Fri, 22 Sep 2023 21:34:13 +0200 Subject: i2c: i801: Replace magic value with constant in dmi_check_onboard_devices Replace magic number 10 with the appropriate constant. Signed-off-by: Heiner Kallweit Reviewed-by: Andi Shyti Signed-off-by: Andi Shyti --- drivers/i2c/busses/i2c-i801.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index 3932e8d96a17..8af944bd89f2 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -1117,7 +1117,7 @@ static void dmi_check_onboard_devices(const struct dmi_header *dm, void *adap) { int i, count; - if (dm->type != 10) + if (dm->type != DMI_ENTRY_ONBOARD_DEVICE) return; count = (dm->length - sizeof(struct dmi_header)) / 2; -- cgit v1.2.3 From 4810603ce35482865c2f88d0cc09ff5caca12120 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Fri, 22 Sep 2023 21:35:00 +0200 Subject: i2c: i801: Remove unused argument from tco functions Argument priv isn't used, so remove it. Signed-off-by: Heiner Kallweit Reviewed-by: Andi Shyti Signed-off-by: Andi Shyti --- drivers/i2c/busses/i2c-i801.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index 8af944bd89f2..b9b850b69b73 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -1445,8 +1445,7 @@ static inline void i801_del_mux(struct i801_priv *priv) { } #endif static struct platform_device * -i801_add_tco_spt(struct i801_priv *priv, struct pci_dev *pci_dev, - struct resource *tco_res) +i801_add_tco_spt(struct pci_dev *pci_dev, struct resource *tco_res) { static const struct itco_wdt_platform_data pldata = { .name = "Intel PCH", @@ -1477,8 +1476,7 @@ i801_add_tco_spt(struct i801_priv *priv, struct pci_dev *pci_dev, } static struct platform_device * -i801_add_tco_cnl(struct i801_priv *priv, struct pci_dev *pci_dev, - struct resource *tco_res) +i801_add_tco_cnl(struct pci_dev *pci_dev, struct resource *tco_res) { static const struct itco_wdt_platform_data pldata = { .name = "Intel PCH", @@ -1518,9 +1516,9 @@ static void i801_add_tco(struct i801_priv *priv) res->flags = IORESOURCE_IO; if (priv->features & FEATURE_TCO_CNL) - priv->tco_pdev = i801_add_tco_cnl(priv, pci_dev, tco_res); + priv->tco_pdev = i801_add_tco_cnl(pci_dev, tco_res); else - priv->tco_pdev = i801_add_tco_spt(priv, pci_dev, tco_res); + priv->tco_pdev = i801_add_tco_spt(pci_dev, tco_res); if (IS_ERR(priv->tco_pdev)) dev_warn(&pci_dev->dev, "failed to create iTCO device\n"); -- cgit v1.2.3 From c0fcc4d9fc0784f8c11af0343092f8d945339431 Mon Sep 17 00:00:00 2001 From: Esben Haabendal Date: Thu, 25 Jan 2024 14:56:36 +0100 Subject: i2c: imx: move to generic GPIO recovery Starting with commit 75820314de26 ("i2c: core: add generic I2C GPIO recovery") GPIO bus recovery is supported by the I2C core, so we can remove the driver implementation and use that one instead. As a nice side-effect, pinctrl becomes optional, allowing bus recovery on LS1021A, which does not have such luxury, but can be wired up to use extra fixed GPIO pins. Note: The previous error messages about bus recovery not being supported is dropped with this change. Given that it is perfectly possible to have platforms where bus recovery works without pinctrl support, I happen to work on one such, both error messages does not really make sense in those cases. And I don't see how to know if this is the case or not. Signed-off-by: Esben Haabendal Acked-by: Oleksij Rempel Signed-off-by: Andi Shyti --- drivers/i2c/busses/i2c-imx.c | 62 ++++---------------------------------------- 1 file changed, 5 insertions(+), 57 deletions(-) diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index 88a053987403..d6ba93fc7fee 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -212,10 +212,6 @@ struct imx_i2c_struct { const struct imx_i2c_hwdata *hwdata; struct i2c_bus_recovery_info rinfo; - struct pinctrl *pinctrl; - struct pinctrl_state *pinctrl_pins_default; - struct pinctrl_state *pinctrl_pins_gpio; - struct imx_i2c_dma *dma; struct i2c_client *slave; enum i2c_slave_event last_slave_event; @@ -1357,24 +1353,6 @@ static int i2c_imx_xfer_atomic(struct i2c_adapter *adapter, return result; } -static void i2c_imx_prepare_recovery(struct i2c_adapter *adap) -{ - struct imx_i2c_struct *i2c_imx; - - i2c_imx = container_of(adap, struct imx_i2c_struct, adapter); - - pinctrl_select_state(i2c_imx->pinctrl, i2c_imx->pinctrl_pins_gpio); -} - -static void i2c_imx_unprepare_recovery(struct i2c_adapter *adap) -{ - struct imx_i2c_struct *i2c_imx; - - i2c_imx = container_of(adap, struct imx_i2c_struct, adapter); - - pinctrl_select_state(i2c_imx->pinctrl, i2c_imx->pinctrl_pins_default); -} - /* * We switch SCL and SDA to their GPIO function and do some bitbanging * for bus recovery. These alternative pinmux settings can be @@ -1385,43 +1363,13 @@ static void i2c_imx_unprepare_recovery(struct i2c_adapter *adap) static int i2c_imx_init_recovery_info(struct imx_i2c_struct *i2c_imx, struct platform_device *pdev) { - struct i2c_bus_recovery_info *rinfo = &i2c_imx->rinfo; - - i2c_imx->pinctrl = devm_pinctrl_get(&pdev->dev); - if (!i2c_imx->pinctrl) { - dev_info(&pdev->dev, "pinctrl unavailable, bus recovery not supported\n"); - return 0; - } - if (IS_ERR(i2c_imx->pinctrl)) { - dev_info(&pdev->dev, "can't get pinctrl, bus recovery not supported\n"); - return PTR_ERR(i2c_imx->pinctrl); - } - - i2c_imx->pinctrl_pins_default = pinctrl_lookup_state(i2c_imx->pinctrl, - PINCTRL_STATE_DEFAULT); - i2c_imx->pinctrl_pins_gpio = pinctrl_lookup_state(i2c_imx->pinctrl, - "gpio"); - rinfo->sda_gpiod = devm_gpiod_get_optional(&pdev->dev, "sda", GPIOD_IN); - rinfo->scl_gpiod = devm_gpiod_get(&pdev->dev, "scl", GPIOD_OUT_HIGH_OPEN_DRAIN); - - if (PTR_ERR(rinfo->sda_gpiod) == -EPROBE_DEFER || - PTR_ERR(rinfo->scl_gpiod) == -EPROBE_DEFER) { - return -EPROBE_DEFER; - } else if (IS_ERR(rinfo->sda_gpiod) || - IS_ERR(rinfo->scl_gpiod) || - IS_ERR(i2c_imx->pinctrl_pins_default) || - IS_ERR(i2c_imx->pinctrl_pins_gpio)) { - dev_dbg(&pdev->dev, "recovery information incomplete\n"); - return 0; - } + struct i2c_bus_recovery_info *bri = &i2c_imx->rinfo; - dev_dbg(&pdev->dev, "using scl%s for recovery\n", - rinfo->sda_gpiod ? ",sda" : ""); + bri->pinctrl = devm_pinctrl_get(&pdev->dev); + if (IS_ERR(bri->pinctrl)) + return PTR_ERR(bri->pinctrl); - rinfo->prepare_recovery = i2c_imx_prepare_recovery; - rinfo->unprepare_recovery = i2c_imx_unprepare_recovery; - rinfo->recover_bus = i2c_generic_scl_recovery; - i2c_imx->adapter.bus_recovery_info = rinfo; + i2c_imx->adapter.bus_recovery_info = bri; return 0; } -- cgit v1.2.3 From 7a7fd335f821f93f9327f581e1677361c48a0dea Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 31 Jan 2024 17:09:30 +0100 Subject: i2c: sh_mobile: Switch R-Mobile A1/APE6 and SH-Mobile AG5 to new frequency calculation Switch the R-Mobile A1, R-Mobile APE6, and SH-Mobile AG5 SoCs to the new frequency calculation formula, to (a) avoid running the I2C bus too fast, and (b) bring the low/high ratio closer to the recommended ratio 5/4. As this makes fast_clock_dt_config and v2_freq_calc_dt_config identical, merge them into a single fast_clock_dt_config. Legacy SH users (sh7343, sh7366, and sh772[234]) are left alone, and still use the old formula. Measurement results on R-Mobile APE6 and SH-Mobile AG5 (fck=104 MHz, clks_per_count=2): 100 kHz: 106 kHz LH=1.12 before, 99.6 kHz L/H=1.22 after 400 kHz: 384 kHz LH=1.67 before, 392 kHz L/H=1.27 after Measurement results on R-Mobile A1 (fck=49.5 MHz, clks_per_count=1): 100 kHz: 106 kHz L/H=1.09 before, 99.6 kHz L/H=1.20 after Signed-off-by: Geert Uytterhoeven Reviewed-by: Wolfram Sang Signed-off-by: Andi Shyti --- drivers/i2c/busses/i2c-sh_mobile.c | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c index 5adbe62cf621..c65ac3d7eadc 100644 --- a/drivers/i2c/busses/i2c-sh_mobile.c +++ b/drivers/i2c/busses/i2c-sh_mobile.c @@ -773,7 +773,7 @@ static int sh_mobile_i2c_r8a7740_workaround(struct sh_mobile_i2c_data *pd) iic_wr(pd, ICCR, ICCR_TRS); udelay(10); - return sh_mobile_i2c_init(pd); + return sh_mobile_i2c_v2_init(pd); } static const struct sh_mobile_dt_config default_dt_config = { @@ -782,11 +782,6 @@ static const struct sh_mobile_dt_config default_dt_config = { }; static const struct sh_mobile_dt_config fast_clock_dt_config = { - .clks_per_count = 2, - .setup = sh_mobile_i2c_init, -}; - -static const struct sh_mobile_dt_config v2_freq_calc_dt_config = { .clks_per_count = 2, .setup = sh_mobile_i2c_v2_init, }; @@ -799,17 +794,17 @@ static const struct sh_mobile_dt_config r8a7740_dt_config = { static const struct of_device_id sh_mobile_i2c_dt_ids[] = { { .compatible = "renesas,iic-r8a73a4", .data = &fast_clock_dt_config }, { .compatible = "renesas,iic-r8a7740", .data = &r8a7740_dt_config }, - { .compatible = "renesas,iic-r8a774c0", .data = &v2_freq_calc_dt_config }, - { .compatible = "renesas,iic-r8a7790", .data = &v2_freq_calc_dt_config }, - { .compatible = "renesas,iic-r8a7791", .data = &v2_freq_calc_dt_config }, - { .compatible = "renesas,iic-r8a7792", .data = &v2_freq_calc_dt_config }, - { .compatible = "renesas,iic-r8a7793", .data = &v2_freq_calc_dt_config }, - { .compatible = "renesas,iic-r8a7794", .data = &v2_freq_calc_dt_config }, - { .compatible = "renesas,iic-r8a7795", .data = &v2_freq_calc_dt_config }, - { .compatible = "renesas,iic-r8a77990", .data = &v2_freq_calc_dt_config }, + { .compatible = "renesas,iic-r8a774c0", .data = &fast_clock_dt_config }, + { .compatible = "renesas,iic-r8a7790", .data = &fast_clock_dt_config }, + { .compatible = "renesas,iic-r8a7791", .data = &fast_clock_dt_config }, + { .compatible = "renesas,iic-r8a7792", .data = &fast_clock_dt_config }, + { .compatible = "renesas,iic-r8a7793", .data = &fast_clock_dt_config }, + { .compatible = "renesas,iic-r8a7794", .data = &fast_clock_dt_config }, + { .compatible = "renesas,iic-r8a7795", .data = &fast_clock_dt_config }, + { .compatible = "renesas,iic-r8a77990", .data = &fast_clock_dt_config }, { .compatible = "renesas,iic-sh73a0", .data = &fast_clock_dt_config }, - { .compatible = "renesas,rcar-gen2-iic", .data = &v2_freq_calc_dt_config }, - { .compatible = "renesas,rcar-gen3-iic", .data = &v2_freq_calc_dt_config }, + { .compatible = "renesas,rcar-gen2-iic", .data = &fast_clock_dt_config }, + { .compatible = "renesas,rcar-gen3-iic", .data = &fast_clock_dt_config }, { .compatible = "renesas,rmobile-iic", .data = &default_dt_config }, {}, }; -- cgit v1.2.3 From 5c015726a266b33227bc91a6926b3e93de834117 Mon Sep 17 00:00:00 2001 From: Devyn Liu Date: Thu, 1 Feb 2024 14:13:44 +0800 Subject: i2c: hisi: Optimized the value setting of maxwrite limit to fifo depth - 1 The driver finishes a write cycle by read the fifo tx full status or write limit decrease to 0. The driver starts to write data to the FIFO after the I2C FIFO almost empty interrupt is reported. The threshold for FIFO almost empty interrupt is that the amount of data in the FIFO is less than or equal to 1. Reduce write maxwrite to the fifo depth - aempty interrupt threshold. Limiting the number of data to be written at a time to remaining fifo capacity. Signed-off-by: Devyn Liu Reviewed-by: Yicong Yang Reviewed-by: Andi Shyti Signed-off-by: Andi Shyti --- drivers/i2c/busses/i2c-hisi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-hisi.c b/drivers/i2c/busses/i2c-hisi.c index dfad5bad5075..cd37053362ee 100644 --- a/drivers/i2c/busses/i2c-hisi.c +++ b/drivers/i2c/busses/i2c-hisi.c @@ -266,7 +266,7 @@ static int hisi_i2c_read_rx_fifo(struct hisi_i2c_controller *ctlr) static void hisi_i2c_xfer_msg(struct hisi_i2c_controller *ctlr) { - int max_write = HISI_I2C_TX_FIFO_DEPTH; + int max_write = HISI_I2C_TX_FIFO_DEPTH - HISI_I2C_TX_F_AE_THRESH; bool need_restart = false, last_msg; struct i2c_msg *cur_msg; u32 cmd, fifo_state; -- cgit v1.2.3 From 9911be1d372946924b1fedaa1b96574920f35519 Mon Sep 17 00:00:00 2001 From: Devyn Liu Date: Thu, 1 Feb 2024 14:13:45 +0800 Subject: i2c: hisi: Add clearing tx aempty interrupt operation The driver receives the tx fifo almost empty(aempty) interrupt and reads the tx_aempty_int_mstat to start a round of data transfer. The operation of clearing the TX aempty interrupt after completing a write cycle is added to ensure that the FIFO is truly at almost empty status when an aempty interrupt is received. The threshold for fifo almost empty interrupt is defined as 1. Signed-off-by: Devyn Liu Reviewed-by: Yicong Yang Reviewed-by: Andi Shyti Signed-off-by: Andi Shyti --- drivers/i2c/busses/i2c-hisi.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/i2c/busses/i2c-hisi.c b/drivers/i2c/busses/i2c-hisi.c index cd37053362ee..975c0b1c44de 100644 --- a/drivers/i2c/busses/i2c-hisi.c +++ b/drivers/i2c/busses/i2c-hisi.c @@ -57,6 +57,8 @@ #define HISI_I2C_FS_SPK_LEN_CNT GENMASK(7, 0) #define HISI_I2C_HS_SPK_LEN 0x003c #define HISI_I2C_HS_SPK_LEN_CNT GENMASK(7, 0) +#define HISI_I2C_TX_INT_CLR 0x0040 +#define HISI_I2C_TX_AEMPTY_INT BIT(0) #define HISI_I2C_INT_MSTAT 0x0044 #define HISI_I2C_INT_CLR 0x0048 #define HISI_I2C_INT_MASK 0x004C @@ -124,6 +126,11 @@ static void hisi_i2c_clear_int(struct hisi_i2c_controller *ctlr, u32 mask) writel_relaxed(mask, ctlr->iobase + HISI_I2C_INT_CLR); } +static void hisi_i2c_clear_tx_int(struct hisi_i2c_controller *ctlr, u32 mask) +{ + writel_relaxed(mask, ctlr->iobase + HISI_I2C_TX_INT_CLR); +} + static void hisi_i2c_handle_errors(struct hisi_i2c_controller *ctlr) { u32 int_err = ctlr->xfer_err, reg; @@ -168,6 +175,7 @@ static int hisi_i2c_start_xfer(struct hisi_i2c_controller *ctlr) writel(reg, ctlr->iobase + HISI_I2C_FIFO_CTRL); hisi_i2c_clear_int(ctlr, HISI_I2C_INT_ALL); + hisi_i2c_clear_tx_int(ctlr, HISI_I2C_TX_AEMPTY_INT); hisi_i2c_enable_int(ctlr, HISI_I2C_INT_ALL); return 0; @@ -323,6 +331,8 @@ static void hisi_i2c_xfer_msg(struct hisi_i2c_controller *ctlr) */ if (ctlr->msg_tx_idx == ctlr->msg_num) hisi_i2c_disable_int(ctlr, HISI_I2C_INT_TX_EMPTY); + + hisi_i2c_clear_tx_int(ctlr, HISI_I2C_TX_AEMPTY_INT); } static irqreturn_t hisi_i2c_irq(int irq, void *context) @@ -363,6 +373,7 @@ out: if (int_stat & HISI_I2C_INT_TRANS_CPLT) { hisi_i2c_disable_int(ctlr, HISI_I2C_INT_ALL); hisi_i2c_clear_int(ctlr, HISI_I2C_INT_ALL); + hisi_i2c_clear_tx_int(ctlr, HISI_I2C_TX_AEMPTY_INT); complete(ctlr->completion); } -- cgit v1.2.3 From d9a9657a1343a7eef7bcbf6e974bf4cea75121a5 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 1 Feb 2024 13:18:51 +0100 Subject: dt-bindings: i2c: renesas,rcar-i2c: Add r8a779h0 support Document support for the I2C Bus Interfaces in the Renesas R-Car V4M (R8A779H0) SoC. Signed-off-by: Geert Uytterhoeven Acked-by: Conor Dooley Acked-by: Andi Shyti Signed-off-by: Andi Shyti --- Documentation/devicetree/bindings/i2c/renesas,rcar-i2c.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/i2c/renesas,rcar-i2c.yaml b/Documentation/devicetree/bindings/i2c/renesas,rcar-i2c.yaml index c4ace5585e1e..51b220da461b 100644 --- a/Documentation/devicetree/bindings/i2c/renesas,rcar-i2c.yaml +++ b/Documentation/devicetree/bindings/i2c/renesas,rcar-i2c.yaml @@ -53,6 +53,7 @@ properties: - renesas,i2c-r8a779a0 # R-Car V3U - renesas,i2c-r8a779f0 # R-Car S4-8 - renesas,i2c-r8a779g0 # R-Car V4H + - renesas,i2c-r8a779h0 # R-Car V4M - const: renesas,rcar-gen4-i2c # R-Car Gen4 reg: -- cgit v1.2.3 From ea4f32970b69aa0eb3c74ae23662f97366bf6968 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Fri, 2 Feb 2024 08:01:41 +0100 Subject: i2c: i801: Define FEATURES_ICH5 as an extension of FEATURES_ICH4 This change simplifies the code a little and makes clearer that the ICH5 feature set is an extension of the ICH4 feature set. Signed-off-by: Heiner Kallweit Reviewed-by: Andi Shyti Signed-off-by: Andi Shyti --- drivers/i2c/busses/i2c-i801.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index b9b850b69b73..44ae6326d88c 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -969,11 +969,10 @@ static const struct i2c_algorithm smbus_algorithm = { .functionality = i801_func, }; -#define FEATURES_ICH5 (FEATURE_BLOCK_PROC | FEATURE_I2C_BLOCK_READ | \ - FEATURE_IRQ | FEATURE_SMBUS_PEC | \ - FEATURE_BLOCK_BUFFER | FEATURE_HOST_NOTIFY) #define FEATURES_ICH4 (FEATURE_SMBUS_PEC | FEATURE_BLOCK_BUFFER | \ FEATURE_HOST_NOTIFY) +#define FEATURES_ICH5 (FEATURES_ICH4 | FEATURE_BLOCK_PROC | \ + FEATURE_I2C_BLOCK_READ | FEATURE_IRQ) static const struct pci_device_id i801_ids[] = { { PCI_DEVICE_DATA(INTEL, 82801AA_3, 0) }, -- cgit v1.2.3 From 03f9863b1afa37779e82b18999a5e16aa759a397 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Fri, 2 Feb 2024 08:02:19 +0100 Subject: i2c: i801: Add helper i801_check_and_clear_pec_error Avoid code duplication and factor out checking and clearing PEC error bit to new helper i801_check_and_clear_pec_error(). Signed-off-by: Heiner Kallweit Reviewed-by: Andi Shyti Signed-off-by: Andi Shyti --- drivers/i2c/busses/i2c-i801.c | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index 44ae6326d88c..156bace92ad3 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -328,11 +328,27 @@ MODULE_PARM_DESC(disable_features, "Disable selected driver features:\n" "\t\t 0x10 don't use interrupts\n" "\t\t 0x20 disable SMBus Host Notify "); +static int i801_check_and_clear_pec_error(struct i801_priv *priv) +{ + u8 status; + + if (!(priv->features & FEATURE_SMBUS_PEC)) + return 0; + + status = inb_p(SMBAUXSTS(priv)) & SMBAUXSTS_CRCE; + if (status) { + outb_p(status, SMBAUXSTS(priv)); + return -EBADMSG; + } + + return 0; +} + /* Make sure the SMBus host is ready to start transmitting. Return 0 if it is, -EBUSY if it is not. */ static int i801_check_pre(struct i801_priv *priv) { - int status; + int status, result; status = inb_p(SMBHSTSTS(priv)); if (status & SMBHSTSTS_HOST_BUSY) { @@ -353,13 +369,9 @@ static int i801_check_pre(struct i801_priv *priv) * the hardware was already in this state when the driver * started. */ - if (priv->features & FEATURE_SMBUS_PEC) { - status = inb_p(SMBAUXSTS(priv)) & SMBAUXSTS_CRCE; - if (status) { - pci_dbg(priv->pci_dev, "Clearing aux status flags (%02x)\n", status); - outb_p(status, SMBAUXSTS(priv)); - } - } + result = i801_check_and_clear_pec_error(priv); + if (result) + pci_dbg(priv->pci_dev, "Clearing aux status flag CRCE\n"); return 0; } @@ -408,14 +420,12 @@ static int i801_check_post(struct i801_priv *priv, int status) * bit is harmless as long as it's cleared before * the next operation. */ - if ((priv->features & FEATURE_SMBUS_PEC) && - (inb_p(SMBAUXSTS(priv)) & SMBAUXSTS_CRCE)) { - outb_p(SMBAUXSTS_CRCE, SMBAUXSTS(priv)); - result = -EBADMSG; - dev_dbg(&priv->pci_dev->dev, "PEC error\n"); + result = i801_check_and_clear_pec_error(priv); + if (result) { + pci_dbg(priv->pci_dev, "PEC error\n"); } else { result = -ENXIO; - dev_dbg(&priv->pci_dev->dev, "No response\n"); + pci_dbg(priv->pci_dev, "No response\n"); } } if (status & SMBHSTSTS_BUS_ERR) { -- cgit v1.2.3 From 6ff9d46cd36fbd004b1cd7c9173af5cdfbcd1df1 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Fri, 2 Feb 2024 08:02:55 +0100 Subject: i2c: i801: Split i801_block_transaction i2c and smbus block transaction handling have little in common, therefore split this function to improve code readability. Signed-off-by: Heiner Kallweit Reviewed-by: Andi Shyti Signed-off-by: Andi Shyti --- drivers/i2c/busses/i2c-i801.c | 112 +++++++++++++++++++----------------------- 1 file changed, 50 insertions(+), 62 deletions(-) diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index 156bace92ad3..24eb187dbc1f 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -802,77 +802,65 @@ static int i801_simple_transaction(struct i801_priv *priv, union i2c_smbus_data return 0; } -/* Block transaction function */ -static int i801_block_transaction(struct i801_priv *priv, union i2c_smbus_data *data, - u8 addr, u8 hstcmd, char read_write, int command) +static int i801_smbus_block_transaction(struct i801_priv *priv, union i2c_smbus_data *data, + u8 addr, u8 hstcmd, char read_write, int command) { - int result = 0; - unsigned char hostc; - if (read_write == I2C_SMBUS_READ && command == I2C_SMBUS_BLOCK_DATA) data->block[0] = I2C_SMBUS_BLOCK_MAX; else if (data->block[0] < 1 || data->block[0] > I2C_SMBUS_BLOCK_MAX) return -EPROTO; - switch (command) { - case I2C_SMBUS_BLOCK_DATA: - i801_set_hstadd(priv, addr, read_write); - outb_p(hstcmd, SMBHSTCMD(priv)); - break; - case I2C_SMBUS_I2C_BLOCK_DATA: - /* - * NB: page 240 of ICH5 datasheet shows that the R/#W - * bit should be cleared here, even when reading. - * However if SPD Write Disable is set (Lynx Point and later), - * the read will fail if we don't set the R/#W bit. - */ - i801_set_hstadd(priv, addr, - priv->original_hstcfg & SMBHSTCFG_SPD_WD ? - read_write : I2C_SMBUS_WRITE); - if (read_write == I2C_SMBUS_READ) { - /* NB: page 240 of ICH5 datasheet also shows - * that DATA1 is the cmd field when reading - */ - outb_p(hstcmd, SMBHSTDAT1(priv)); - } else - outb_p(hstcmd, SMBHSTCMD(priv)); - - if (read_write == I2C_SMBUS_WRITE) { - /* set I2C_EN bit in configuration register */ - pci_read_config_byte(priv->pci_dev, SMBHSTCFG, &hostc); - pci_write_config_byte(priv->pci_dev, SMBHSTCFG, - hostc | SMBHSTCFG_I2C_EN); - } else if (!(priv->features & FEATURE_I2C_BLOCK_READ)) { - dev_err(&priv->pci_dev->dev, - "I2C block read is unsupported!\n"); - return -EOPNOTSUPP; - } - break; - case I2C_SMBUS_BLOCK_PROC_CALL: + if (command == I2C_SMBUS_BLOCK_PROC_CALL) /* Needs to be flagged as write transaction */ i801_set_hstadd(priv, addr, I2C_SMBUS_WRITE); + else + i801_set_hstadd(priv, addr, read_write); + outb_p(hstcmd, SMBHSTCMD(priv)); + + if (priv->features & FEATURE_BLOCK_BUFFER) + return i801_block_transaction_by_block(priv, data, read_write, command); + else + return i801_block_transaction_byte_by_byte(priv, data, read_write, command); +} + +static int i801_i2c_block_transaction(struct i801_priv *priv, union i2c_smbus_data *data, + u8 addr, u8 hstcmd, char read_write, int command) +{ + int result; + u8 hostc; + + if (data->block[0] < 1 || data->block[0] > I2C_SMBUS_BLOCK_MAX) + return -EPROTO; + /* + * NB: page 240 of ICH5 datasheet shows that the R/#W bit should be cleared here, + * even when reading. However if SPD Write Disable is set (Lynx Point and later), + * the read will fail if we don't set the R/#W bit. + */ + i801_set_hstadd(priv, addr, + priv->original_hstcfg & SMBHSTCFG_SPD_WD ? read_write : I2C_SMBUS_WRITE); + + /* NB: page 240 of ICH5 datasheet shows that DATA1 is the cmd field when reading */ + if (read_write == I2C_SMBUS_READ) + outb_p(hstcmd, SMBHSTDAT1(priv)); + else outb_p(hstcmd, SMBHSTCMD(priv)); - break; + + if (read_write == I2C_SMBUS_WRITE) { + /* set I2C_EN bit in configuration register */ + pci_read_config_byte(priv->pci_dev, SMBHSTCFG, &hostc); + pci_write_config_byte(priv->pci_dev, SMBHSTCFG, hostc | SMBHSTCFG_I2C_EN); + } else if (!(priv->features & FEATURE_I2C_BLOCK_READ)) { + pci_err(priv->pci_dev, "I2C block read is unsupported!\n"); + return -EOPNOTSUPP; } - /* Experience has shown that the block buffer can only be used for - SMBus (not I2C) block transactions, even though the datasheet - doesn't mention this limitation. */ - if ((priv->features & FEATURE_BLOCK_BUFFER) && - command != I2C_SMBUS_I2C_BLOCK_DATA) - result = i801_block_transaction_by_block(priv, data, - read_write, - command); - else - result = i801_block_transaction_byte_by_byte(priv, data, - read_write, - command); + /* Block buffer isn't supported for I2C block transactions */ + result = i801_block_transaction_byte_by_byte(priv, data, read_write, command); - if (command == I2C_SMBUS_I2C_BLOCK_DATA - && read_write == I2C_SMBUS_WRITE) { - /* restore saved configuration register value */ + /* restore saved configuration register value */ + if (read_write == I2C_SMBUS_WRITE) pci_write_config_byte(priv->pci_dev, SMBHSTCFG, hostc); - } + return result; } @@ -903,10 +891,10 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr, outb_p(inb_p(SMBAUXCTL(priv)) & (~SMBAUXCTL_CRC), SMBAUXCTL(priv)); - if (size == I2C_SMBUS_BLOCK_DATA || - size == I2C_SMBUS_I2C_BLOCK_DATA || - size == I2C_SMBUS_BLOCK_PROC_CALL) - ret = i801_block_transaction(priv, data, addr, command, read_write, size); + if (size == I2C_SMBUS_BLOCK_DATA || size == I2C_SMBUS_BLOCK_PROC_CALL) + ret = i801_smbus_block_transaction(priv, data, addr, command, read_write, size); + else if (size == I2C_SMBUS_I2C_BLOCK_DATA) + ret = i801_i2c_block_transaction(priv, data, addr, command, read_write, size); else ret = i801_simple_transaction(priv, data, addr, command, read_write, size); -- cgit v1.2.3 From 29dae4572efb676e4831eee68bc423b9f32c05d0 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Fri, 2 Feb 2024 08:04:06 +0100 Subject: i2c: i801: Add SMBUS_LEN_SENTINEL Add a sentinel length value that is used to check whether we should read and use the length value provided by the slave device. This simplifies the currently used checks. Signed-off-by: Heiner Kallweit Reviewed-by: Andi Shyti Signed-off-by: Andi Shyti --- drivers/i2c/busses/i2c-i801.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index 24eb187dbc1f..15d251288317 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -205,6 +205,8 @@ #define STATUS_FLAGS (SMBHSTSTS_BYTE_DONE | SMBHSTSTS_INTR | \ STATUS_ERROR_FLAGS) +#define SMBUS_LEN_SENTINEL (I2C_SMBUS_BLOCK_MAX + 1) + /* Older devices have their ID defined in */ #define PCI_DEVICE_ID_INTEL_COMETLAKE_SMBUS 0x02a3 #define PCI_DEVICE_ID_INTEL_COMETLAKE_H_SMBUS 0x06a3 @@ -541,9 +543,12 @@ out: static void i801_isr_byte_done(struct i801_priv *priv) { if (priv->is_read) { - /* For SMBus block reads, length is received with first byte */ - if (((priv->cmd & 0x1c) == I801_BLOCK_DATA) && - (priv->count == 0)) { + /* + * At transfer start i801_smbus_block_transaction() marks + * the block length as invalid. Check for this sentinel value + * and read the block length from SMBHSTDAT0. + */ + if (priv->len == SMBUS_LEN_SENTINEL) { priv->len = inb_p(SMBHSTDAT0(priv)); if (priv->len < 1 || priv->len > I2C_SMBUS_BLOCK_MAX) { dev_err(&priv->pci_dev->dev, @@ -698,8 +703,12 @@ static int i801_block_transaction_byte_by_byte(struct i801_priv *priv, if (status) return status; - if (i == 1 && read_write == I2C_SMBUS_READ - && command != I2C_SMBUS_I2C_BLOCK_DATA) { + /* + * At transfer start i801_smbus_block_transaction() marks + * the block length as invalid. Check for this sentinel value + * and read the block length from SMBHSTDAT0. + */ + if (len == SMBUS_LEN_SENTINEL) { len = inb_p(SMBHSTDAT0(priv)); if (len < 1 || len > I2C_SMBUS_BLOCK_MAX) { dev_err(&priv->pci_dev->dev, @@ -806,7 +815,8 @@ static int i801_smbus_block_transaction(struct i801_priv *priv, union i2c_smbus_ u8 addr, u8 hstcmd, char read_write, int command) { if (read_write == I2C_SMBUS_READ && command == I2C_SMBUS_BLOCK_DATA) - data->block[0] = I2C_SMBUS_BLOCK_MAX; + /* Mark block length as invalid */ + data->block[0] = SMBUS_LEN_SENTINEL; else if (data->block[0] < 1 || data->block[0] > I2C_SMBUS_BLOCK_MAX) return -EPROTO; -- cgit v1.2.3 From 857cc04cdf508d043a062fff3aea8ca01303b731 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Fri, 2 Feb 2024 08:06:30 +0100 Subject: i2c: i801: Add helper i801_get_block_len Avoid code duplication and factor out retrieving and checking the block length value to new helper i801_get_block_len(). Signed-off-by: Heiner Kallweit Reviewed-by: Andi Shyti Signed-off-by: Andi Shyti --- drivers/i2c/busses/i2c-i801.c | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index 15d251288317..918c794c77a4 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -330,6 +330,18 @@ MODULE_PARM_DESC(disable_features, "Disable selected driver features:\n" "\t\t 0x10 don't use interrupts\n" "\t\t 0x20 disable SMBus Host Notify "); +static int i801_get_block_len(struct i801_priv *priv) +{ + u8 len = inb_p(SMBHSTDAT0(priv)); + + if (len < 1 || len > I2C_SMBUS_BLOCK_MAX) { + pci_err(priv->pci_dev, "Illegal SMBus block read size %u\n", len); + return -EPROTO; + } + + return len; +} + static int i801_check_and_clear_pec_error(struct i801_priv *priv) { u8 status; @@ -525,12 +537,11 @@ static int i801_block_transaction_by_block(struct i801_priv *priv, if (read_write == I2C_SMBUS_READ || command == I2C_SMBUS_BLOCK_PROC_CALL) { - len = inb_p(SMBHSTDAT0(priv)); - if (len < 1 || len > I2C_SMBUS_BLOCK_MAX) { - status = -EPROTO; + status = i801_get_block_len(priv); + if (status < 0) goto out; - } + len = status; data->block[0] = len; for (i = 0; i < len; i++) data->block[i + 1] = inb_p(SMBBLKDAT(priv)); @@ -549,14 +560,11 @@ static void i801_isr_byte_done(struct i801_priv *priv) * and read the block length from SMBHSTDAT0. */ if (priv->len == SMBUS_LEN_SENTINEL) { - priv->len = inb_p(SMBHSTDAT0(priv)); - if (priv->len < 1 || priv->len > I2C_SMBUS_BLOCK_MAX) { - dev_err(&priv->pci_dev->dev, - "Illegal SMBus block read size %d\n", - priv->len); + priv->len = i801_get_block_len(priv); + if (priv->len < 0) /* FIXME: Recover */ priv->len = I2C_SMBUS_BLOCK_MAX; - } + priv->data[-1] = priv->len; } @@ -709,11 +717,8 @@ static int i801_block_transaction_byte_by_byte(struct i801_priv *priv, * and read the block length from SMBHSTDAT0. */ if (len == SMBUS_LEN_SENTINEL) { - len = inb_p(SMBHSTDAT0(priv)); - if (len < 1 || len > I2C_SMBUS_BLOCK_MAX) { - dev_err(&priv->pci_dev->dev, - "Illegal SMBus block read size %d\n", - len); + len = i801_get_block_len(priv); + if (len < 0) { /* Recover */ while (inb_p(SMBHSTSTS(priv)) & SMBHSTSTS_HOST_BUSY) -- cgit v1.2.3 From 48acf8292280f257fb0047478153a81471ee7f4d Mon Sep 17 00:00:00 2001 From: Rand Deeb Date: Tue, 6 Feb 2024 22:42:01 +0300 Subject: i2c: Remove redundant comparison in npcm_i2c_reg_slave In the npcm_i2c_reg_slave() function, there was a redundant comparison that checked if 'bus->slave' was null immediately after assigning it the 'client' value. There were concerns about a potential null dereference because of `client->adapter`, but according to Wolfram Sang, "we trusted ourselves here" Therefore, this comparison is unnecessary. Found by Linux Verification Center (linuxtesting.org) with SVACE. Signed-off-by: Rand Deeb Reviewed-by: Wolfram Sang Signed-off-by: Andi Shyti --- drivers/i2c/busses/i2c-npcm7xx.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/i2c/busses/i2c-npcm7xx.c b/drivers/i2c/busses/i2c-npcm7xx.c index 54181b3f1919..2fe68615942e 100644 --- a/drivers/i2c/busses/i2c-npcm7xx.c +++ b/drivers/i2c/busses/i2c-npcm7xx.c @@ -1264,9 +1264,6 @@ static int npcm_i2c_reg_slave(struct i2c_client *client) bus->slave = client; - if (!bus->slave) - return -EINVAL; - if (client->flags & I2C_CLIENT_TEN) return -EAFNOSUPPORT; -- cgit v1.2.3 From 2a8d18cd63dc5175c67a42fe8cb7cd3cd465d845 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Mon, 26 Feb 2024 15:09:10 +0800 Subject: dt-bindings: i2c: imx-lpi2c: add i.MX95 LPI2C Add i.MX95 LPI2C compatible entry, same as i.MX93 compatible with i.MX7ULP. Acked-by: Conor Dooley Signed-off-by: Peng Fan Signed-off-by: Andi Shyti --- Documentation/devicetree/bindings/i2c/i2c-imx-lpi2c.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/i2c/i2c-imx-lpi2c.yaml b/Documentation/devicetree/bindings/i2c/i2c-imx-lpi2c.yaml index 4656f5112b84..54d500be6aaa 100644 --- a/Documentation/devicetree/bindings/i2c/i2c-imx-lpi2c.yaml +++ b/Documentation/devicetree/bindings/i2c/i2c-imx-lpi2c.yaml @@ -24,6 +24,7 @@ properties: - fsl,imx8qm-lpi2c - fsl,imx8ulp-lpi2c - fsl,imx93-lpi2c + - fsl,imx95-lpi2c - const: fsl,imx7ulp-lpi2c reg: -- cgit v1.2.3 From a856c9e6104f7b4619f09e19ab95903c7888da96 Mon Sep 17 00:00:00 2001 From: Varshini Rajendran Date: Fri, 23 Feb 2024 22:54:59 +0530 Subject: dt-bindings: i2c: at91: Add sam9x7 compatible string Add compatible string for sam9x7. Signed-off-by: Varshini Rajendran Acked-by: Conor Dooley Signed-off-by: Andi Shyti --- Documentation/devicetree/bindings/i2c/atmel,at91sam-i2c.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/i2c/atmel,at91sam-i2c.yaml b/Documentation/devicetree/bindings/i2c/atmel,at91sam-i2c.yaml index 6adedd3ec399..b1c13bab2472 100644 --- a/Documentation/devicetree/bindings/i2c/atmel,at91sam-i2c.yaml +++ b/Documentation/devicetree/bindings/i2c/atmel,at91sam-i2c.yaml @@ -25,7 +25,9 @@ properties: - atmel,sama5d2-i2c - microchip,sam9x60-i2c - items: - - const: microchip,sama7g5-i2c + - enum: + - microchip,sama7g5-i2c + - microchip,sam9x7-i2c - const: microchip,sam9x60-i2c reg: -- cgit v1.2.3 From 535677e44d57a31e1363529b5ecddb92653d7136 Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Tue, 13 Feb 2024 14:48:42 +0200 Subject: i2c: designware: Uniform initialization flow for polling mode Currently initialization flow in i2c_dw_probe_master() skips a few steps and has code duplication for polling mode implementation. Simplify this by adding a new ACCESS_POLLING flag that is set for those two platforms that currently use polling mode and use it to skip interrupt handler setup. Signed-off-by: Jarkko Nikula Tested-by: Jiawen Wu Signed-off-by: Andi Shyti --- drivers/i2c/busses/i2c-designware-core.h | 1 + drivers/i2c/busses/i2c-designware-master.c | 42 ++++++----------------------- drivers/i2c/busses/i2c-designware-pcidrv.c | 2 +- drivers/i2c/busses/i2c-designware-platdrv.c | 2 +- 4 files changed, 11 insertions(+), 36 deletions(-) diff --git a/drivers/i2c/busses/i2c-designware-core.h b/drivers/i2c/busses/i2c-designware-core.h index a7f6f3eafad7..78c8062a8eb5 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 ACCESS_INTR_MASK BIT(0) #define ACCESS_NO_IRQ_SUSPEND BIT(1) #define ARBITRATION_SEMAPHORE BIT(2) +#define ACCESS_POLLING BIT(3) #define MODEL_MSCC_OCELOT BIT(8) #define MODEL_BAIKAL_BT1 BIT(9) diff --git a/drivers/i2c/busses/i2c-designware-master.c b/drivers/i2c/busses/i2c-designware-master.c index 85dbd0eb5392..e879a0f5cc97 100644 --- a/drivers/i2c/busses/i2c-designware-master.c +++ b/drivers/i2c/busses/i2c-designware-master.c @@ -953,31 +953,6 @@ static int i2c_dw_init_recovery_info(struct dw_i2c_dev *dev) return 0; } -static int i2c_dw_poll_adap_quirk(struct dw_i2c_dev *dev) -{ - struct i2c_adapter *adap = &dev->adapter; - int ret; - - pm_runtime_get_noresume(dev->dev); - ret = i2c_add_numbered_adapter(adap); - if (ret) - dev_err(dev->dev, "Failed to add adapter: %d\n", ret); - pm_runtime_put_noidle(dev->dev); - - return ret; -} - -static bool i2c_dw_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; @@ -1033,9 +1008,6 @@ int i2c_dw_probe_master(struct dw_i2c_dev *dev) adap->dev.parent = dev->dev; i2c_set_adapdata(adap, dev); - if (i2c_dw_is_model_poll(dev)) - return i2c_dw_poll_adap_quirk(dev); - if (dev->flags & ACCESS_NO_IRQ_SUSPEND) { irq_flags = IRQF_NO_SUSPEND; } else { @@ -1049,12 +1021,14 @@ int i2c_dw_probe_master(struct dw_i2c_dev *dev) regmap_write(dev->map, DW_IC_INTR_MASK, 0); i2c_dw_release_lock(dev); - ret = devm_request_irq(dev->dev, dev->irq, i2c_dw_isr, irq_flags, - dev_name(dev->dev), dev); - if (ret) { - dev_err(dev->dev, "failure requesting irq %i: %d\n", - dev->irq, ret); - return ret; + if (!(dev->flags & ACCESS_POLLING)) { + ret = devm_request_irq(dev->dev, dev->irq, i2c_dw_isr, + irq_flags, dev_name(dev->dev), dev); + if (ret) { + dev_err(dev->dev, "failure requesting irq %i: %d\n", + dev->irq, ret); + return ret; + } } ret = i2c_dw_init_recovery_info(dev); diff --git a/drivers/i2c/busses/i2c-designware-pcidrv.c b/drivers/i2c/busses/i2c-designware-pcidrv.c index 61d7a27aa070..9be9a2658e1f 100644 --- a/drivers/i2c/busses/i2c-designware-pcidrv.c +++ b/drivers/i2c/busses/i2c-designware-pcidrv.c @@ -154,7 +154,7 @@ static int navi_amd_setup(struct pci_dev *pdev, struct dw_pci_controller *c) { struct dw_i2c_dev *dev = dev_get_drvdata(&pdev->dev); - dev->flags |= MODEL_AMD_NAVI_GPU; + dev->flags |= MODEL_AMD_NAVI_GPU | ACCESS_POLLING; dev->timings.bus_freq_hz = I2C_MAX_STANDARD_MODE_FREQ; return 0; } diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c index 855b698e99c0..4ab41ba39d55 100644 --- a/drivers/i2c/busses/i2c-designware-platdrv.c +++ b/drivers/i2c/busses/i2c-designware-platdrv.c @@ -290,7 +290,7 @@ static int dw_i2c_plat_probe(struct platform_device *pdev) dev->flags = (uintptr_t)device_get_match_data(&pdev->dev); if (device_property_present(&pdev->dev, "wx,i2c-snps-model")) - dev->flags = MODEL_WANGXUN_SP; + dev->flags = MODEL_WANGXUN_SP | ACCESS_POLLING; dev->dev = &pdev->dev; dev->irq = irq; -- cgit v1.2.3 From 64b6426a6e97a95c044fd2fff3f0adf7c1edb60c Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Tue, 13 Feb 2024 14:48:43 +0200 Subject: i2c: designware: Do not enable interrupts shortly in polling mode I was testing the polling mode txgbe_i2c_dw_xfer_quirk() on a HW where the i2c-designware has interrupt connected and shared with other device. I noticed there is a bogus interrupt for each transfer. Reason for this that both polling mode functions call the i2c_dw_xfer_init() which enable interrupts then followed by immediate disable by the same polling mode functions. This is enough to trigger TX_EMPTY interrupt. Fix this by introducing a __i2c_dw_write_intr_mask() helper that unmasks interrupts conditionally and use it in i2c_dw_xfer_init(). Signed-off-by: Jarkko Nikula Tested-by: Jiawen Wu Signed-off-by: Andi Shyti --- drivers/i2c/busses/i2c-designware-core.h | 8 ++++++++ drivers/i2c/busses/i2c-designware-master.c | 4 +--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/i2c/busses/i2c-designware-core.h b/drivers/i2c/busses/i2c-designware-core.h index 78c8062a8eb5..8ce6111cdda3 100644 --- a/drivers/i2c/busses/i2c-designware-core.h +++ b/drivers/i2c/busses/i2c-designware-core.h @@ -352,6 +352,14 @@ static inline void __i2c_dw_disable_nowait(struct dw_i2c_dev *dev) dev->status &= ~STATUS_ACTIVE; } +static inline void __i2c_dw_write_intr_mask(struct dw_i2c_dev *dev, + unsigned int intr_mask) +{ + unsigned int val = dev->flags & ACCESS_POLLING ? 0 : intr_mask; + + regmap_write(dev->map, DW_IC_INTR_MASK, val); +} + void __i2c_dw_disable(struct dw_i2c_dev *dev); extern void i2c_dw_configure_master(struct dw_i2c_dev *dev); diff --git a/drivers/i2c/busses/i2c-designware-master.c b/drivers/i2c/busses/i2c-designware-master.c index e879a0f5cc97..835d82e2c5fe 100644 --- a/drivers/i2c/busses/i2c-designware-master.c +++ b/drivers/i2c/busses/i2c-designware-master.c @@ -250,7 +250,7 @@ static void i2c_dw_xfer_init(struct dw_i2c_dev *dev) /* Clear and enable interrupts */ regmap_read(dev->map, DW_IC_CLR_INTR, &dummy); - regmap_write(dev->map, DW_IC_INTR_MASK, DW_IC_INTR_MASTER_MASK); + __i2c_dw_write_intr_mask(dev, DW_IC_INTR_MASTER_MASK); } static int i2c_dw_check_stopbit(struct dw_i2c_dev *dev) @@ -300,7 +300,6 @@ static int amd_i2c_dw_xfer_quirk(struct i2c_adapter *adap, struct i2c_msg *msgs, dev->msgs = msgs; dev->msgs_num = num_msgs; i2c_dw_xfer_init(dev); - regmap_write(dev->map, DW_IC_INTR_MASK, 0); /* Initiate messages read/write transaction */ for (msg_wrt_idx = 0; msg_wrt_idx < num_msgs; msg_wrt_idx++) { @@ -384,7 +383,6 @@ static int txgbe_i2c_dw_xfer_quirk(struct i2c_adapter *adap, struct i2c_msg *msg 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; -- cgit v1.2.3 From 04c71da4a9f4eef94bec153ed667d105dacffda3 Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Tue, 13 Feb 2024 14:48:44 +0200 Subject: i2c: designware: Use accessors to DW_IC_INTR_MASK register Convert access to DW_IC_INTR_MASK register using the existing __i2c_dw_write_intr_mask() and a __i2c_dw_read_intr_mask() introduced here. Motivation to this is to prepare for generic polling mode code where polling mode will use a SW mask instead of DW_IC_INTR_MASK. Signed-off-by: Jarkko Nikula Tested-by: Jiawen Wu Signed-off-by: Andi Shyti --- drivers/i2c/busses/i2c-designware-common.c | 2 +- drivers/i2c/busses/i2c-designware-core.h | 7 +++++++ drivers/i2c/busses/i2c-designware-master.c | 22 ++++++++++++---------- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/drivers/i2c/busses/i2c-designware-common.c b/drivers/i2c/busses/i2c-designware-common.c index 35f762872b8a..e8a688d04aee 100644 --- a/drivers/i2c/busses/i2c-designware-common.c +++ b/drivers/i2c/busses/i2c-designware-common.c @@ -648,7 +648,7 @@ void i2c_dw_disable(struct dw_i2c_dev *dev) __i2c_dw_disable(dev); /* Disable all interrupts */ - regmap_write(dev->map, DW_IC_INTR_MASK, 0); + __i2c_dw_write_intr_mask(dev, 0); regmap_read(dev->map, DW_IC_CLR_INTR, &dummy); i2c_dw_release_lock(dev); diff --git a/drivers/i2c/busses/i2c-designware-core.h b/drivers/i2c/busses/i2c-designware-core.h index 8ce6111cdda3..4d277ebcca92 100644 --- a/drivers/i2c/busses/i2c-designware-core.h +++ b/drivers/i2c/busses/i2c-designware-core.h @@ -360,6 +360,13 @@ static inline void __i2c_dw_write_intr_mask(struct dw_i2c_dev *dev, regmap_write(dev->map, DW_IC_INTR_MASK, val); } +static inline void __i2c_dw_read_intr_mask(struct dw_i2c_dev *dev, + unsigned int *intr_mask) +{ + if (!(dev->flags & ACCESS_POLLING)) + regmap_read(dev->map, DW_IC_INTR_MASK, intr_mask); +} + void __i2c_dw_disable(struct dw_i2c_dev *dev); extern void i2c_dw_configure_master(struct dw_i2c_dev *dev); diff --git a/drivers/i2c/busses/i2c-designware-master.c b/drivers/i2c/busses/i2c-designware-master.c index 835d82e2c5fe..2e8f9733ddd3 100644 --- a/drivers/i2c/busses/i2c-designware-master.c +++ b/drivers/i2c/busses/i2c-designware-master.c @@ -240,7 +240,7 @@ static void i2c_dw_xfer_init(struct dw_i2c_dev *dev) msgs[dev->msg_write_idx].addr | ic_tar); /* Enforce disabled interrupts (due to HW issues) */ - regmap_write(dev->map, DW_IC_INTR_MASK, 0); + __i2c_dw_write_intr_mask(dev, 0); /* Enable the adapter */ __i2c_dw_enable(dev); @@ -544,7 +544,7 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev) if (dev->msg_err) intr_mask = 0; - regmap_write(dev->map, DW_IC_INTR_MASK, intr_mask); + __i2c_dw_write_intr_mask(dev, intr_mask); } static u8 @@ -552,6 +552,7 @@ i2c_dw_recv_len(struct dw_i2c_dev *dev, u8 len) { struct i2c_msg *msgs = dev->msgs; u32 flags = msgs[dev->msg_read_idx].flags; + unsigned int intr_mask; /* * Adjust the buffer length and mask the flag @@ -566,8 +567,9 @@ i2c_dw_recv_len(struct dw_i2c_dev *dev, u8 len) * Received buffer length, re-enable TX_EMPTY interrupt * to resume the SMBUS transaction. */ - regmap_update_bits(dev->map, DW_IC_INTR_MASK, DW_IC_INTR_TX_EMPTY, - DW_IC_INTR_TX_EMPTY); + __i2c_dw_read_intr_mask(dev, &intr_mask); + intr_mask |= DW_IC_INTR_TX_EMPTY; + __i2c_dw_write_intr_mask(dev, intr_mask); return len; } @@ -827,7 +829,7 @@ static irqreturn_t i2c_dw_isr(int this_irq, void *dev_id) * interrupt really came from this HW (E.g. firmware has left * the HW active). */ - regmap_write(dev->map, DW_IC_INTR_MASK, 0); + __i2c_dw_write_intr_mask(dev, 0); return IRQ_HANDLED; } @@ -840,7 +842,7 @@ static irqreturn_t i2c_dw_isr(int this_irq, void *dev_id) * Anytime TX_ABRT is set, the contents of the tx/rx * buffers are flushed. Make sure to skip them. */ - regmap_write(dev->map, DW_IC_INTR_MASK, 0); + __i2c_dw_write_intr_mask(dev, 0); goto tx_aborted; } @@ -862,9 +864,9 @@ tx_aborted: complete(&dev->cmd_complete); else if (unlikely(dev->flags & ACCESS_INTR_MASK)) { /* Workaround to trigger pending interrupt */ - regmap_read(dev->map, DW_IC_INTR_MASK, &stat); - regmap_write(dev->map, DW_IC_INTR_MASK, 0); - regmap_write(dev->map, DW_IC_INTR_MASK, stat); + __i2c_dw_read_intr_mask(dev, &stat); + __i2c_dw_write_intr_mask(dev, 0); + __i2c_dw_write_intr_mask(dev, stat); } return IRQ_HANDLED; @@ -1016,7 +1018,7 @@ int i2c_dw_probe_master(struct dw_i2c_dev *dev) if (ret) return ret; - regmap_write(dev->map, DW_IC_INTR_MASK, 0); + __i2c_dw_write_intr_mask(dev, 0); i2c_dw_release_lock(dev); if (!(dev->flags & ACCESS_POLLING)) { -- cgit v1.2.3 From bd002efaa16e4cfffc25db2d9c4669aaa2b43646 Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Tue, 13 Feb 2024 14:48:45 +0200 Subject: i2c: designware: Move interrupt handling functions before i2c_dw_xfer() Code is more logically arranged when i2c_dw_read_clear_intrbits() and i2c_dw_isr() are located before i2c_dw_xfer(). Real reason for this is to prepare for more shared code between interrupt and polling mode code. While at it, remove one extra space and refer to the i2c_dw_init_master() in two comment sections. Signed-off-by: Jarkko Nikula Tested-by: Jiawen Wu Signed-off-by: Andi Shyti --- drivers/i2c/busses/i2c-designware-master.c | 226 ++++++++++++++--------------- 1 file changed, 113 insertions(+), 113 deletions(-) diff --git a/drivers/i2c/busses/i2c-designware-master.c b/drivers/i2c/busses/i2c-designware-master.c index 2e8f9733ddd3..7c8e95fef9c8 100644 --- a/drivers/i2c/busses/i2c-designware-master.c +++ b/drivers/i2c/busses/i2c-designware-master.c @@ -633,119 +633,6 @@ i2c_dw_read(struct dw_i2c_dev *dev) } } -/* - * Prepare controller for a transaction and call i2c_dw_xfer_msg. - */ -static int -i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) -{ - struct dw_i2c_dev *dev = i2c_get_adapdata(adap); - int ret; - - dev_dbg(dev->dev, "%s: msgs: %d\n", __func__, num); - - pm_runtime_get_sync(dev->dev); - - /* - * Initiate I2C message transfer when polling mode is enabled, - * As it is polling based transfer mechanism, which does not support - * interrupt based functionalities of existing DesignWare driver. - */ - switch (dev->flags & MODEL_MASK) { - case MODEL_AMD_NAVI_GPU: - ret = amd_i2c_dw_xfer_quirk(adap, msgs, num); - goto done_nolock; - case MODEL_WANGXUN_SP: - ret = txgbe_i2c_dw_xfer_quirk(adap, msgs, num); - goto done_nolock; - default: - break; - } - - reinit_completion(&dev->cmd_complete); - dev->msgs = msgs; - dev->msgs_num = num; - dev->cmd_err = 0; - dev->msg_write_idx = 0; - dev->msg_read_idx = 0; - dev->msg_err = 0; - dev->status = 0; - dev->abort_source = 0; - dev->rx_outstanding = 0; - - ret = i2c_dw_acquire_lock(dev); - if (ret) - goto done_nolock; - - ret = i2c_dw_wait_bus_not_busy(dev); - if (ret < 0) - goto done; - - /* Start the transfers */ - i2c_dw_xfer_init(dev); - - /* Wait for tx to complete */ - if (!wait_for_completion_timeout(&dev->cmd_complete, adap->timeout)) { - dev_err(dev->dev, "controller timed out\n"); - /* i2c_dw_init implicitly disables the adapter */ - i2c_recover_bus(&dev->adapter); - i2c_dw_init_master(dev); - ret = -ETIMEDOUT; - goto done; - } - - /* - * We must disable the adapter before returning and signaling the end - * of the current transfer. Otherwise the hardware might continue - * generating interrupts which in turn causes a race condition with - * the following transfer. Needs some more investigation if the - * additional interrupts are a hardware bug or this driver doesn't - * handle them correctly yet. - */ - __i2c_dw_disable_nowait(dev); - - if (dev->msg_err) { - ret = dev->msg_err; - goto done; - } - - /* No error */ - if (likely(!dev->cmd_err && !dev->status)) { - ret = num; - goto done; - } - - /* We have an error */ - if (dev->cmd_err == DW_IC_ERR_TX_ABRT) { - ret = i2c_dw_handle_tx_abort(dev); - goto done; - } - - if (dev->status) - dev_err(dev->dev, - "transfer terminated early - interrupt latency too high?\n"); - - ret = -EIO; - -done: - i2c_dw_release_lock(dev); - -done_nolock: - pm_runtime_mark_last_busy(dev->dev); - pm_runtime_put_autosuspend(dev->dev); - - return ret; -} - -static const struct i2c_algorithm i2c_dw_algo = { - .master_xfer = i2c_dw_xfer, - .functionality = i2c_dw_func, -}; - -static const struct i2c_adapter_quirks i2c_dw_quirks = { - .flags = I2C_AQ_NO_ZERO_LEN, -}; - static u32 i2c_dw_read_clear_intrbits(struct dw_i2c_dev *dev) { unsigned int stat, dummy; @@ -872,6 +759,119 @@ tx_aborted: return IRQ_HANDLED; } +/* + * Prepare controller for a transaction and call i2c_dw_xfer_msg. + */ +static int +i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) +{ + struct dw_i2c_dev *dev = i2c_get_adapdata(adap); + int ret; + + dev_dbg(dev->dev, "%s: msgs: %d\n", __func__, num); + + pm_runtime_get_sync(dev->dev); + + /* + * Initiate I2C message transfer when polling mode is enabled, + * As it is polling based transfer mechanism, which does not support + * interrupt based functionalities of existing DesignWare driver. + */ + switch (dev->flags & MODEL_MASK) { + case MODEL_AMD_NAVI_GPU: + ret = amd_i2c_dw_xfer_quirk(adap, msgs, num); + goto done_nolock; + case MODEL_WANGXUN_SP: + ret = txgbe_i2c_dw_xfer_quirk(adap, msgs, num); + goto done_nolock; + default: + break; + } + + reinit_completion(&dev->cmd_complete); + dev->msgs = msgs; + dev->msgs_num = num; + dev->cmd_err = 0; + dev->msg_write_idx = 0; + dev->msg_read_idx = 0; + dev->msg_err = 0; + dev->status = 0; + dev->abort_source = 0; + dev->rx_outstanding = 0; + + ret = i2c_dw_acquire_lock(dev); + if (ret) + goto done_nolock; + + ret = i2c_dw_wait_bus_not_busy(dev); + if (ret < 0) + goto done; + + /* Start the transfers */ + i2c_dw_xfer_init(dev); + + /* Wait for tx to complete */ + if (!wait_for_completion_timeout(&dev->cmd_complete, adap->timeout)) { + dev_err(dev->dev, "controller timed out\n"); + /* i2c_dw_init_master() implicitly disables the adapter */ + i2c_recover_bus(&dev->adapter); + i2c_dw_init_master(dev); + ret = -ETIMEDOUT; + goto done; + } + + /* + * We must disable the adapter before returning and signaling the end + * of the current transfer. Otherwise the hardware might continue + * generating interrupts which in turn causes a race condition with + * the following transfer. Needs some more investigation if the + * additional interrupts are a hardware bug or this driver doesn't + * handle them correctly yet. + */ + __i2c_dw_disable_nowait(dev); + + if (dev->msg_err) { + ret = dev->msg_err; + goto done; + } + + /* No error */ + if (likely(!dev->cmd_err && !dev->status)) { + ret = num; + goto done; + } + + /* We have an error */ + if (dev->cmd_err == DW_IC_ERR_TX_ABRT) { + ret = i2c_dw_handle_tx_abort(dev); + goto done; + } + + if (dev->status) + dev_err(dev->dev, + "transfer terminated early - interrupt latency too high?\n"); + + ret = -EIO; + +done: + i2c_dw_release_lock(dev); + +done_nolock: + pm_runtime_mark_last_busy(dev->dev); + pm_runtime_put_autosuspend(dev->dev); + + return ret; +} + +static const struct i2c_algorithm i2c_dw_algo = { + .master_xfer = i2c_dw_xfer, + .functionality = i2c_dw_func, +}; + +static const struct i2c_adapter_quirks i2c_dw_quirks = { + .flags = I2C_AQ_NO_ZERO_LEN, +}; + void i2c_dw_configure_master(struct dw_i2c_dev *dev) { struct i2c_timings *t = &dev->timings; -- cgit v1.2.3 From c94612a72ac87b0337a0d85b9263266776ed4190 Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Tue, 13 Feb 2024 14:48:46 +0200 Subject: i2c: designware: Fix RX FIFO depth define on Wangxun 10Gb NIC I believe RX FIFO depth define 0 is incorrect on Wangxun 10Gb NIC. It must be at least 1 since code is able to read received data from the DW_IC_DATA_CMD register. For now this define is irrelevant since the txgbe_i2c_dw_xfer_quirk() doesn't use the rx_fifo_depth member variable of struct dw_i2c_dev but is needed when converting code into generic polling mode implementation. Signed-off-by: Jarkko Nikula Tested-by: Jiawen Wu Signed-off-by: Andi Shyti --- drivers/i2c/busses/i2c-designware-core.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-designware-core.h b/drivers/i2c/busses/i2c-designware-core.h index 4d277ebcca92..bf8f140dc113 100644 --- a/drivers/i2c/busses/i2c-designware-core.h +++ b/drivers/i2c/busses/i2c-designware-core.h @@ -319,7 +319,7 @@ struct dw_i2c_dev { #define AMD_UCSI_INTR_EN 0xd #define TXGBE_TX_FIFO_DEPTH 4 -#define TXGBE_RX_FIFO_DEPTH 0 +#define TXGBE_RX_FIFO_DEPTH 1 struct i2c_dw_semaphore_callbacks { int (*probe)(struct dw_i2c_dev *dev); -- cgit v1.2.3 From 197ecadad842855437a36ffc161648418ae02a97 Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Tue, 13 Feb 2024 14:48:47 +0200 Subject: i2c: designware: Implement generic polling mode code for Wangxun 10Gb NIC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I got an idea the i2c-designware should not need duplicated state machines for the interrupt and polling modes. The IP is practically the same and state transitions happens in response to the events that can be observed from the DW_IC_RAW_INTR_STAT register. Either by interrupts or by polling. Another reasons are the interrupt mode is the most tested, has handling for special cases as well as transmit abort handling and those are missing from two polling mode quirks. Patch implements a generic polling mode by using existing code for interrupt mode. This is done by moving event handling from the i2c_dw_isr() into a new i2c_dw_process_transfer() that will be called both from the i2c_dw_isr() and a polling loop. Polling loop is implemented in a new i2c_dw_wait_transfer() that is shared between both modes. In interrupt mode it waits for the completion object as before. In polling mode both completion object and DW_IC_RAW_INTR_STAT are polled to determine completed transfer and state transitions. Loop tries to save power by sleeping "stetson guessed" range between 3 and 25 µS which falls between 10 cycles of High-speed mode 3.4 Mb/s and Fast mode 400 kHz. With it the CPU usage was reduced under heavy Fast mode I2C transfer without much increase in total transfer time but otherwise no more effort has been put to optimize this. I decided to convert the txgbe_i2c_dw_xfer_quirk() straight to generic polling mode code in this patch. It doesn't have HW dependent quirks like the amd_i2c_dw_xfer_quirk() does have and without users this patch is needless. Signed-off-by: Jarkko Nikula Tested-by: Jiawen Wu Signed-off-by: Andi Shyti --- drivers/i2c/busses/i2c-designware-core.h | 5 + drivers/i2c/busses/i2c-designware-master.c | 172 ++++++++++++----------------- 2 files changed, 75 insertions(+), 102 deletions(-) diff --git a/drivers/i2c/busses/i2c-designware-core.h b/drivers/i2c/busses/i2c-designware-core.h index bf8f140dc113..e9606c00b8d1 100644 --- a/drivers/i2c/busses/i2c-designware-core.h +++ b/drivers/i2c/busses/i2c-designware-core.h @@ -212,6 +212,7 @@ struct reset_control; * @msg_err: error status of the current transfer * @status: i2c master status, one of STATUS_* * @abort_source: copy of the TX_ABRT_SOURCE register + * @sw_mask: SW mask of DW_IC_INTR_MASK used in polling mode * @irq: interrupt number for the i2c master * @flags: platform specific flags like type of IO accessors or model * @adapter: i2c subsystem adapter node @@ -270,6 +271,7 @@ struct dw_i2c_dev { int msg_err; unsigned int status; unsigned int abort_source; + unsigned int sw_mask; int irq; u32 flags; struct i2c_adapter adapter; @@ -358,6 +360,7 @@ static inline void __i2c_dw_write_intr_mask(struct dw_i2c_dev *dev, unsigned int val = dev->flags & ACCESS_POLLING ? 0 : intr_mask; regmap_write(dev->map, DW_IC_INTR_MASK, val); + dev->sw_mask = intr_mask; } static inline void __i2c_dw_read_intr_mask(struct dw_i2c_dev *dev, @@ -365,6 +368,8 @@ static inline void __i2c_dw_read_intr_mask(struct dw_i2c_dev *dev, { if (!(dev->flags & ACCESS_POLLING)) regmap_read(dev->map, DW_IC_INTR_MASK, intr_mask); + else + *intr_mask = dev->sw_mask; } void __i2c_dw_disable(struct dw_i2c_dev *dev); diff --git a/drivers/i2c/busses/i2c-designware-master.c b/drivers/i2c/busses/i2c-designware-master.c index 7c8e95fef9c8..c7e56002809a 100644 --- a/drivers/i2c/busses/i2c-designware-master.c +++ b/drivers/i2c/busses/i2c-designware-master.c @@ -354,67 +354,6 @@ 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); - - 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 @@ -649,7 +588,12 @@ static u32 i2c_dw_read_clear_intrbits(struct dw_i2c_dev *dev) * * The raw version might be useful for debugging purposes. */ - regmap_read(dev->map, DW_IC_INTR_STAT, &stat); + if (!(dev->flags & ACCESS_POLLING)) { + regmap_read(dev->map, DW_IC_INTR_STAT, &stat); + } else { + regmap_read(dev->map, DW_IC_RAW_INTR_STAT, &stat); + stat &= dev->sw_mask; + } /* * Do not use the IC_CLR_INTR register to clear interrupts, or @@ -689,37 +633,8 @@ static u32 i2c_dw_read_clear_intrbits(struct dw_i2c_dev *dev) return stat; } -/* - * Interrupt service routine. This gets called whenever an I2C master interrupt - * occurs. - */ -static irqreturn_t i2c_dw_isr(int this_irq, void *dev_id) +static void i2c_dw_process_transfer(struct dw_i2c_dev *dev, unsigned int stat) { - struct dw_i2c_dev *dev = dev_id; - unsigned int stat, enabled; - - regmap_read(dev->map, DW_IC_ENABLE, &enabled); - regmap_read(dev->map, DW_IC_RAW_INTR_STAT, &stat); - if (!enabled || !(stat & ~DW_IC_INTR_ACTIVITY)) - return IRQ_NONE; - if (pm_runtime_suspended(dev->dev) || stat == GENMASK(31, 0)) - return IRQ_NONE; - dev_dbg(dev->dev, "enabled=%#x stat=%#x\n", enabled, stat); - - stat = i2c_dw_read_clear_intrbits(dev); - - if (!(dev->status & STATUS_ACTIVE)) { - /* - * Unexpected interrupt in driver point of view. State - * variables are either unset or stale so acknowledge and - * disable interrupts for suppressing further interrupts if - * interrupt really came from this HW (E.g. firmware has left - * the HW active). - */ - __i2c_dw_write_intr_mask(dev, 0); - return IRQ_HANDLED; - } - if (stat & DW_IC_INTR_TX_ABRT) { dev->cmd_err |= DW_IC_ERR_TX_ABRT; dev->status &= ~STATUS_MASK; @@ -755,10 +670,71 @@ tx_aborted: __i2c_dw_write_intr_mask(dev, 0); __i2c_dw_write_intr_mask(dev, stat); } +} + +/* + * Interrupt service routine. This gets called whenever an I2C master interrupt + * occurs. + */ +static irqreturn_t i2c_dw_isr(int this_irq, void *dev_id) +{ + struct dw_i2c_dev *dev = dev_id; + unsigned int stat, enabled; + + regmap_read(dev->map, DW_IC_ENABLE, &enabled); + regmap_read(dev->map, DW_IC_RAW_INTR_STAT, &stat); + if (!enabled || !(stat & ~DW_IC_INTR_ACTIVITY)) + return IRQ_NONE; + if (pm_runtime_suspended(dev->dev) || stat == GENMASK(31, 0)) + return IRQ_NONE; + dev_dbg(dev->dev, "enabled=%#x stat=%#x\n", enabled, stat); + + stat = i2c_dw_read_clear_intrbits(dev); + + if (!(dev->status & STATUS_ACTIVE)) { + /* + * Unexpected interrupt in driver point of view. State + * variables are either unset or stale so acknowledge and + * disable interrupts for suppressing further interrupts if + * interrupt really came from this HW (E.g. firmware has left + * the HW active). + */ + __i2c_dw_write_intr_mask(dev, 0); + return IRQ_HANDLED; + } + + i2c_dw_process_transfer(dev, stat); return IRQ_HANDLED; } +static int i2c_dw_wait_transfer(struct dw_i2c_dev *dev) +{ + unsigned long timeout = dev->adapter.timeout; + unsigned int stat; + int ret; + + if (!(dev->flags & ACCESS_POLLING)) { + ret = wait_for_completion_timeout(&dev->cmd_complete, timeout); + } else { + timeout += jiffies; + do { + ret = try_wait_for_completion(&dev->cmd_complete); + if (ret) + break; + + stat = i2c_dw_read_clear_intrbits(dev); + if (stat) + i2c_dw_process_transfer(dev, stat); + else + /* Try save some power */ + usleep_range(3, 25); + } while (time_before(jiffies, timeout)); + } + + return ret ? 0 : -ETIMEDOUT; +} + /* * Prepare controller for a transaction and call i2c_dw_xfer_msg. */ @@ -772,18 +748,10 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) pm_runtime_get_sync(dev->dev); - /* - * Initiate I2C message transfer when polling mode is enabled, - * As it is polling based transfer mechanism, which does not support - * interrupt based functionalities of existing DesignWare driver. - */ switch (dev->flags & MODEL_MASK) { case MODEL_AMD_NAVI_GPU: ret = amd_i2c_dw_xfer_quirk(adap, msgs, num); goto done_nolock; - case MODEL_WANGXUN_SP: - ret = txgbe_i2c_dw_xfer_quirk(adap, msgs, num); - goto done_nolock; default: break; } @@ -811,12 +779,12 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) i2c_dw_xfer_init(dev); /* Wait for tx to complete */ - if (!wait_for_completion_timeout(&dev->cmd_complete, adap->timeout)) { + ret = i2c_dw_wait_transfer(dev); + if (ret) { dev_err(dev->dev, "controller timed out\n"); /* i2c_dw_init_master() implicitly disables the adapter */ i2c_recover_bus(&dev->adapter); i2c_dw_init_master(dev); - ret = -ETIMEDOUT; goto done; } -- cgit v1.2.3 From eb52034436a58e742ceea0dcf2f003f83a3449a5 Mon Sep 17 00:00:00 2001 From: "Ricardo B. Marliere" Date: Mon, 19 Feb 2024 09:38:47 -0300 Subject: i2c: constify the struct device_type usage Since commit aed65af1cc2f ("drivers: make device_type const"), the driver core can properly handle constant struct device_type. Move the i2c_adapter_type and i2c_client_type variables to be constant structures as well, placing it into read-only memory which can not be modified at runtime. Signed-off-by: Ricardo B. Marliere Signed-off-by: Wolfram Sang --- drivers/i2c/i2c-core-base.c | 4 ++-- include/linux/i2c.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c index 3bd48d4b6318..ff5c486a1dbb 100644 --- a/drivers/i2c/i2c-core-base.c +++ b/drivers/i2c/i2c-core-base.c @@ -701,7 +701,7 @@ const struct bus_type i2c_bus_type = { }; EXPORT_SYMBOL_GPL(i2c_bus_type); -struct device_type i2c_client_type = { +const struct device_type i2c_client_type = { .groups = i2c_dev_groups, .uevent = i2c_device_uevent, .release = i2c_client_dev_release, @@ -1343,7 +1343,7 @@ static struct attribute *i2c_adapter_attrs[] = { }; ATTRIBUTE_GROUPS(i2c_adapter); -struct device_type i2c_adapter_type = { +const struct device_type i2c_adapter_type = { .groups = i2c_adapter_groups, .release = i2c_adapter_dev_release, }; diff --git a/include/linux/i2c.h b/include/linux/i2c.h index 652ecb7abeda..ff93ff8b257c 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -24,8 +24,8 @@ #include extern const struct bus_type i2c_bus_type; -extern struct device_type i2c_adapter_type; -extern struct device_type i2c_client_type; +extern const struct device_type i2c_adapter_type; +extern const struct device_type i2c_client_type; /* --- General options ------------------------------------------------ */ -- cgit v1.2.3 From 8821c8376993c271a77d241a4e657099f7400974 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Sat, 2 Mar 2024 21:39:13 +0100 Subject: i2c: smbus: Prepare i2c_register_spd for usage on muxed segments If this is an adapter on a muxed bus segment, assume that each segment is connected to a subset of the (> 8) overall memory slots. In this case let's probe the maximum of 8 slots, however stop if the number of overall populated slots is reached. If we're not on a muxed segment and the total number of slots is > 8, then warn because then not all SPD eeproms can be addressed. Presumably the bus is muxed, but the mux config is missing. Signed-off-by: Heiner Kallweit [wsa: removed a superfluous printout] Signed-off-by: Wolfram Sang --- drivers/i2c/i2c-smbus.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/drivers/i2c/i2c-smbus.c b/drivers/i2c/i2c-smbus.c index 74807c6db596..97f338b123b1 100644 --- a/drivers/i2c/i2c-smbus.c +++ b/drivers/i2c/i2c-smbus.c @@ -351,13 +351,18 @@ void i2c_register_spd(struct i2c_adapter *adap) if (!dimm_count) return; - dev_info(&adap->dev, "%d/%d memory slots populated (from DMI)\n", - dimm_count, slot_count); - - if (slot_count > 8) { - dev_warn(&adap->dev, - "Systems with more than 8 memory slots not supported yet, not instantiating SPD\n"); - return; + /* + * If we're a child adapter on a muxed segment, then limit slots to 8, + * as this is the max number of SPD EEPROMs that can be addressed per bus. + */ + if (i2c_parent_is_i2c_adapter(adap)) { + slot_count = 8; + } else { + if (slot_count > 8) { + dev_warn(&adap->dev, + "More than 8 memory slots on a single bus, contact i801 maintainer to add missing mux config\n"); + return; + } } /* -- cgit v1.2.3 From f9ccb4533bdcf31f1225a9a09805329b8020a4e3 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Thu, 29 Feb 2024 11:58:11 +0100 Subject: dt-bindings: i2c: mpc: use proper binding for transfer timeouts "i2c-scl-clk-low-timeout-us" has flaws in itself and the usage here is all wrong. The driver doesn't use it as a maximum time for clock stretching but the maximum time for a total transfer. We already have a binding for the latter. Convert the wrong binding from examples. Signed-off-by: Wolfram Sang Reviewed-by: Chris Packham Tested-by: Chris Packham Acked-by: Rob Herring Signed-off-by: Andi Shyti --- Documentation/devicetree/bindings/i2c/i2c-mpc.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/i2c/i2c-mpc.yaml b/Documentation/devicetree/bindings/i2c/i2c-mpc.yaml index 70fb69b923c4..b1d7d14c0be4 100644 --- a/Documentation/devicetree/bindings/i2c/i2c-mpc.yaml +++ b/Documentation/devicetree/bindings/i2c/i2c-mpc.yaml @@ -96,6 +96,6 @@ examples: interrupts = <43 2>; interrupt-parent = <&mpic>; clock-frequency = <400000>; - i2c-scl-clk-low-timeout-us = <10000>; + i2c-transfer-timeout-us = <10000>; }; ... -- cgit v1.2.3 From 401a8e9e3d697b75c2e237b9b405bb0f388dd7ed Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Thu, 29 Feb 2024 11:58:12 +0100 Subject: i2c: mpc: use proper binding for transfer timeouts "i2c-scl-clk-low-timeout-us" is wrongly used here because it describes maximum clock stretching not maximum transfer time. Additionally, it is deprecated because of issues. Move this driver to the correct binding. Signed-off-by: Wolfram Sang Reviewed-by: Chris Packham Tested-by: Chris Packham Signed-off-by: Andi Shyti --- drivers/i2c/busses/i2c-mpc.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/i2c/busses/i2c-mpc.c b/drivers/i2c/busses/i2c-mpc.c index e4e4995ab224..0b4de9e569ba 100644 --- a/drivers/i2c/busses/i2c-mpc.c +++ b/drivers/i2c/busses/i2c-mpc.c @@ -844,14 +844,14 @@ static int fsl_i2c_probe(struct platform_device *op) mpc_i2c_setup_8xxx(op->dev.of_node, i2c, clock); } - /* - * "fsl,timeout" has been marked as deprecated and, to maintain - * backward compatibility, we will only look for it if - * "i2c-scl-clk-low-timeout-us" is not present. - */ + /* Sadly, we have to support two deprecated bindings here */ result = of_property_read_u32(op->dev.of_node, - "i2c-scl-clk-low-timeout-us", + "i2c-transfer-timeout-us", &mpc_ops.timeout); + if (result == -EINVAL) + result = of_property_read_u32(op->dev.of_node, + "i2c-scl-clk-low-timeout-us", + &mpc_ops.timeout); if (result == -EINVAL) result = of_property_read_u32(op->dev.of_node, "fsl,timeout", &mpc_ops.timeout); -- cgit v1.2.3 From d0e944150446d8056a050049a8f0e98241ba6194 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Thu, 29 Feb 2024 11:58:13 +0100 Subject: i2c: mpc: remove outdated macro DRV_NAME was useful back in the days. But here, being used once, it is only cruft. Signed-off-by: Wolfram Sang Reviewed-by: Chris Packham Tested-by: Chris Packham Signed-off-by: Andi Shyti --- drivers/i2c/busses/i2c-mpc.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/i2c/busses/i2c-mpc.c b/drivers/i2c/busses/i2c-mpc.c index 0b4de9e569ba..8d73c0f405ed 100644 --- a/drivers/i2c/busses/i2c-mpc.c +++ b/drivers/i2c/busses/i2c-mpc.c @@ -30,8 +30,6 @@ #include #include -#define DRV_NAME "mpc-i2c" - #define MPC_I2C_CLOCK_LEGACY 0 #define MPC_I2C_CLOCK_PRESERVE (~0U) @@ -960,7 +958,7 @@ static struct platform_driver mpc_i2c_driver = { .probe = fsl_i2c_probe, .remove_new = fsl_i2c_remove, .driver = { - .name = DRV_NAME, + .name = "mpc-i2c", .of_match_table = mpc_i2c_of_match, .pm = &mpc_i2c_pm_ops, }, -- cgit v1.2.3 From 747bdf912e22732e8de9bd04a2d3e387055604a8 Mon Sep 17 00:00:00 2001 From: Ji Sheng Teoh Date: Fri, 19 Jan 2024 09:33:26 +0800 Subject: i2c: cadence: Add system suspend and resume PM support Enable device system suspend and resume PM support, and mark the device state as suspended during system suspend to reject any data transfer. Signed-off-by: Ji Sheng Teoh Reviewed-by: Andi Shyti Signed-off-by: Andi Shyti --- drivers/i2c/busses/i2c-cadence.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/drivers/i2c/busses/i2c-cadence.c b/drivers/i2c/busses/i2c-cadence.c index de3f58b60dce..4bb7d6756947 100644 --- a/drivers/i2c/busses/i2c-cadence.c +++ b/drivers/i2c/busses/i2c-cadence.c @@ -1176,6 +1176,18 @@ static int __maybe_unused cdns_i2c_runtime_suspend(struct device *dev) return 0; } +static int __maybe_unused cdns_i2c_suspend(struct device *dev) +{ + struct cdns_i2c *xi2c = dev_get_drvdata(dev); + + i2c_mark_adapter_suspended(&xi2c->adap); + + if (!pm_runtime_status_suspended(dev)) + return cdns_i2c_runtime_suspend(dev); + + return 0; +} + /** * cdns_i2c_init - Controller initialisation * @id: Device private data structure @@ -1219,7 +1231,28 @@ static int __maybe_unused cdns_i2c_runtime_resume(struct device *dev) return 0; } +static int __maybe_unused cdns_i2c_resume(struct device *dev) +{ + struct cdns_i2c *xi2c = dev_get_drvdata(dev); + int err; + + err = cdns_i2c_runtime_resume(dev); + if (err) + return err; + + if (pm_runtime_status_suspended(dev)) { + err = cdns_i2c_runtime_suspend(dev); + if (err) + return err; + } + + i2c_mark_adapter_resumed(&xi2c->adap); + + return 0; +} + static const struct dev_pm_ops cdns_i2c_dev_pm_ops = { + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(cdns_i2c_suspend, cdns_i2c_resume) SET_RUNTIME_PM_OPS(cdns_i2c_runtime_suspend, cdns_i2c_runtime_resume, NULL) }; -- cgit v1.2.3 From 188542964e7823713e80416829a054fb867c7158 Mon Sep 17 00:00:00 2001 From: Carlos Song Date: Mon, 26 Feb 2024 13:54:18 +0800 Subject: i2c: imx-lpi2c: add generic GPIO recovery for LPI2C This adds i2c bus recovery to the lpi2c driver. Uses the generic recovery function setting the SCL/SDA pads as GPIO pins and sending 9 clocks to try and recover the bus. Signed-off-by: Carlos Song Reviewed-by: Clark Wang Reviewed-by: Dong Aisheng Signed-off-by: Andi Shyti --- drivers/i2c/busses/i2c-imx-lpi2c.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/drivers/i2c/busses/i2c-imx-lpi2c.c b/drivers/i2c/busses/i2c-imx-lpi2c.c index 678b30e90492..6d72e4e126dd 100644 --- a/drivers/i2c/busses/i2c-imx-lpi2c.c +++ b/drivers/i2c/busses/i2c-imx-lpi2c.c @@ -106,6 +106,7 @@ struct lpi2c_imx_struct { unsigned int txfifosize; unsigned int rxfifosize; enum lpi2c_imx_mode mode; + struct i2c_bus_recovery_info rinfo; }; static void lpi2c_imx_intctrl(struct lpi2c_imx_struct *lpi2c_imx, @@ -133,6 +134,8 @@ static int lpi2c_imx_bus_busy(struct lpi2c_imx_struct *lpi2c_imx) if (time_after(jiffies, orig_jiffies + msecs_to_jiffies(500))) { dev_dbg(&lpi2c_imx->adapter.dev, "bus not work\n"); + if (lpi2c_imx->adapter.bus_recovery_info) + i2c_recover_bus(&lpi2c_imx->adapter); return -ETIMEDOUT; } schedule(); @@ -190,6 +193,8 @@ static void lpi2c_imx_stop(struct lpi2c_imx_struct *lpi2c_imx) if (time_after(jiffies, orig_jiffies + msecs_to_jiffies(500))) { dev_dbg(&lpi2c_imx->adapter.dev, "stop timeout\n"); + if (lpi2c_imx->adapter.bus_recovery_info) + i2c_recover_bus(&lpi2c_imx->adapter); break; } schedule(); @@ -325,6 +330,8 @@ static int lpi2c_imx_txfifo_empty(struct lpi2c_imx_struct *lpi2c_imx) if (time_after(jiffies, orig_jiffies + msecs_to_jiffies(500))) { dev_dbg(&lpi2c_imx->adapter.dev, "txfifo empty timeout\n"); + if (lpi2c_imx->adapter.bus_recovery_info) + i2c_recover_bus(&lpi2c_imx->adapter); return -ETIMEDOUT; } schedule(); @@ -526,6 +533,20 @@ static irqreturn_t lpi2c_imx_isr(int irq, void *dev_id) return IRQ_HANDLED; } +static int lpi2c_imx_init_recovery_info(struct lpi2c_imx_struct *lpi2c_imx, + struct platform_device *pdev) +{ + struct i2c_bus_recovery_info *bri = &lpi2c_imx->rinfo; + + bri->pinctrl = devm_pinctrl_get(&pdev->dev); + if (IS_ERR(bri->pinctrl)) + return PTR_ERR(bri->pinctrl); + + lpi2c_imx->adapter.bus_recovery_info = bri; + + return 0; +} + static u32 lpi2c_imx_func(struct i2c_adapter *adapter) { return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | @@ -600,6 +621,12 @@ static int lpi2c_imx_probe(struct platform_device *pdev) lpi2c_imx->txfifosize = 1 << (temp & 0x0f); lpi2c_imx->rxfifosize = 1 << ((temp >> 8) & 0x0f); + /* Init optional bus recovery function */ + ret = lpi2c_imx_init_recovery_info(lpi2c_imx, pdev); + /* Give it another chance if pinctrl used is not ready yet */ + if (ret == -EPROBE_DEFER) + goto rpm_disable; + ret = i2c_add_adapter(&lpi2c_imx->adapter); if (ret) goto rpm_disable; -- cgit v1.2.3 From 5d85665181beb6d75a0e9e0652f41bd0acb877df Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 8 Jan 2024 15:24:20 +0100 Subject: i2c: rcar: Prepare for the advent of ARCH_RCAR_GEN4 Currently, all Kconfig symbols for R-Car Gen4 SoCs select ARCH_RCAR_GEN3, which might confuse the casual reader. Prepare for the advent of ARCH_RCAR_GEN4 by extending the dependency for auto-selecting reset controller support. Signed-off-by: Geert Uytterhoeven Reviewed-by: Wolfram Sang Signed-off-by: Andi Shyti --- drivers/i2c/busses/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 28eb48dd5b32..97989c914260 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -1235,7 +1235,7 @@ config I2C_RCAR depends on ARCH_RENESAS || COMPILE_TEST select I2C_SLAVE select I2C_SMBUS - select RESET_CONTROLLER if ARCH_RCAR_GEN3 + select RESET_CONTROLLER if ARCH_RCAR_GEN3 || ARCH_RCAR_GEN4 help If you say yes to this option, support will be included for the R-Car I2C controller. -- cgit v1.2.3 From f311507c5336ad6b9ca7687c35c3bd7a4fe2868c Mon Sep 17 00:00:00 2001 From: "Hsin-Yu.Chen" Date: Wed, 6 Mar 2024 12:19:00 +0800 Subject: i2c: remove redundant condition I2C_M_RD is defined as and guaranteed to be 1 and 'flag & I2C_M_RD' is one or zero. No need for an additional condition to obtain the value. Signed-off-by: Hsin-Yu.Chen Reviewed-by: Andi Shyti [wsa: slightly updated commit message] Signed-off-by: Wolfram Sang --- include/linux/i2c.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/i2c.h b/include/linux/i2c.h index ff93ff8b257c..5e6cd43a6dbd 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -931,7 +931,7 @@ static inline int i2c_adapter_id(struct i2c_adapter *adap) static inline u8 i2c_8bit_addr_from_msg(const struct i2c_msg *msg) { - return (msg->addr << 1) | (msg->flags & I2C_M_RD ? 1 : 0); + return (msg->addr << 1) | (msg->flags & I2C_M_RD); } u8 *i2c_get_dma_safe_msg_buf(struct i2c_msg *msg, unsigned int threshold); -- cgit v1.2.3 From 68a04aeebefa7f53f001d69e8595a5c1c4565417 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Mon, 4 Mar 2024 21:44:21 +0100 Subject: Documentation: i2c: Document that client auto-detection is a legacy mechanism Class-based client auto-detection has been considered a legacy mechanism for 10 yrs now. See commit 0c176170089c ("i2c: add deprecation warning for class based instantiation"). Change the documentation of how to write an i2c client accordingly. Signed-off-by: Heiner Kallweit Signed-off-by: Wolfram Sang --- Documentation/i2c/writing-clients.rst | 32 ++++---------------------------- 1 file changed, 4 insertions(+), 28 deletions(-) diff --git a/Documentation/i2c/writing-clients.rst b/Documentation/i2c/writing-clients.rst index 41ddc10f1ac7..0b8439ea954c 100644 --- a/Documentation/i2c/writing-clients.rst +++ b/Documentation/i2c/writing-clients.rst @@ -48,10 +48,6 @@ driver model device node, and its I2C address. .id_table = foo_idtable, .probe = foo_probe, .remove = foo_remove, - /* if device autodetection is needed: */ - .class = I2C_CLASS_SOMETHING, - .detect = foo_detect, - .address_list = normal_i2c, .shutdown = foo_shutdown, /* optional */ .command = foo_command, /* optional, deprecated */ @@ -203,27 +199,8 @@ reference for later use. Device Detection ---------------- -Sometimes you do not know in advance which I2C devices are connected to -a given I2C bus. This is for example the case of hardware monitoring -devices on a PC's SMBus. In that case, you may want to let your driver -detect supported devices automatically. This is how the legacy model -was working, and is now available as an extension to the standard -driver model. - -You simply have to define a detect callback which will attempt to -identify supported devices (returning 0 for supported ones and -ENODEV -for unsupported ones), a list of addresses to probe, and a device type -(or class) so that only I2C buses which may have that type of device -connected (and not otherwise enumerated) will be probed. For example, -a driver for a hardware monitoring chip for which auto-detection is -needed would set its class to I2C_CLASS_HWMON, and only I2C adapters -with a class including I2C_CLASS_HWMON would be probed by this driver. -Note that the absence of matching classes does not prevent the use of -a device of that type on the given I2C adapter. All it prevents is -auto-detection; explicit instantiation of devices is still possible. - -Note that this mechanism is purely optional and not suitable for all -devices. You need some reliable way to identify the supported devices +The device detection mechanism comes with a number of disadvantages. +You need some reliable way to identify the supported devices (typically using device-specific, dedicated identification registers), otherwise misdetections are likely to occur and things can get wrong quickly. Keep in mind that the I2C protocol doesn't include any @@ -231,9 +208,8 @@ standard way to detect the presence of a chip at a given address, let alone a standard way to identify devices. Even worse is the lack of semantics associated to bus transfers, which means that the same transfer can be seen as a read operation by a chip and as a write -operation by another chip. For these reasons, explicit device -instantiation should always be preferred to auto-detection where -possible. +operation by another chip. For these reasons, device detection is +considered a legacy mechanism and shouldn't be used in new code. Device Deletion -- cgit v1.2.3 From 06d0cb6c824c7df736e66060b8c63b0100259d3f Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Wed, 6 Mar 2024 19:02:41 +0100 Subject: i2c: sprd: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new(), which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Acked-by: Chunyan Zhang Signed-off-by: Andi Shyti --- drivers/i2c/busses/i2c-sprd.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/i2c/busses/i2c-sprd.c b/drivers/i2c/busses/i2c-sprd.c index c52d1bec60b4..28c88901d9bc 100644 --- a/drivers/i2c/busses/i2c-sprd.c +++ b/drivers/i2c/busses/i2c-sprd.c @@ -570,7 +570,7 @@ err_rpm_put: return ret; } -static int sprd_i2c_remove(struct platform_device *pdev) +static void sprd_i2c_remove(struct platform_device *pdev) { struct sprd_i2c *i2c_dev = platform_get_drvdata(pdev); int ret; @@ -586,8 +586,6 @@ static int sprd_i2c_remove(struct platform_device *pdev) pm_runtime_put_noidle(i2c_dev->dev); pm_runtime_disable(i2c_dev->dev); - - return 0; } static int __maybe_unused sprd_i2c_suspend_noirq(struct device *dev) @@ -645,7 +643,7 @@ MODULE_DEVICE_TABLE(of, sprd_i2c_of_match); static struct platform_driver sprd_i2c_driver = { .probe = sprd_i2c_probe, - .remove = sprd_i2c_remove, + .remove_new = sprd_i2c_remove, .driver = { .name = "sprd-i2c", .of_match_table = sprd_i2c_of_match, -- cgit v1.2.3