From nobody Mon Oct 6 19:09:49 2025 Received: from mail11.truemail.it (mail11.truemail.it [217.194.8.81]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 725AC14A60D; Fri, 18 Jul 2025 13:43:01 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.194.8.81 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1752846184; cv=none; b=VjCwkKcVCOCmbdbrqcc67bQBsiOzCOJ/AeOzP6h/GRGH6VpUM0RrEnu4/t3rJTLQ6FrdyCF1JslnfcWBKdmCRqo6dKYFcDPXcyFmdfS9H2OlEAk40kKrLFC/InKTJ8P/YTh+CaUNWiwpDd4S/IomFUTv+7jo6Dw7JuC6oGE/6/E= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1752846184; c=relaxed/simple; bh=4o0XLnoYEv06WquFm8G1VOSfBCM6/RJdVDFE27BeSxA=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=Et5AZuPrZoenZG9YnU0QqiPtHHQtTmvef6BqQT9fR3OBzIsHjffN8YTJhht+ZZjSpxgLNek9KQ7B70PLmMaNPScpwkBEa98/ZBp8wvsxDJJktLHYX2zAIwFqdALMMHq4MdDLDTa224clOlIlbzd6ASKUdn89P6Mu2j9Xo4fVsew= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=dolcini.it; spf=pass smtp.mailfrom=dolcini.it; dkim=pass (2048-bit key) header.d=dolcini.it header.i=@dolcini.it header.b=rAq3GYp9; arc=none smtp.client-ip=217.194.8.81 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=dolcini.it Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=dolcini.it Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=dolcini.it header.i=@dolcini.it header.b="rAq3GYp9" Received: from francesco-nb.pivistrello.it (93-49-2-63.ip317.fastwebnet.it [93.49.2.63]) by mail11.truemail.it (Postfix) with ESMTPA id 335BC21150; Fri, 18 Jul 2025 15:34:43 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=dolcini.it; s=default; t=1752845683; bh=e+XXjpP7TXtXVQhaF0YZvfr+9FnAUoMLkIbOnA62APk=; h=From:To:Subject; b=rAq3GYp9bLJjHDp9OxCW+xI6C/88NydvBn3jI+2NQEKIjW/rCvhcGylE7QwzKSMad 4eXiTo6QGIhxI9jYVKCk/sMBDu6Hl2WLQTbetLOs6hdzlWNsow50gngFFjx1iXjgFP amzv7es3gyjRB1r+CahMxnL7ARTk1LTuwKqr73Cumzt8g0sSp11lssIBxArwWCU+sQ iWL+ScHX1EKKZVnQyHpPlP5ocBYmCgH7MbdwHmYwtSNX9t9FOGbgYxV3Cf0vvmiGGP fqmnt0pjIVxLpsEFniZ0Q4gOMGkVUoYyzrvoLEjsle0gq3L/5mQYvMSimUEcIoAcrJ 73gX/ATRDEtXg== From: Francesco Dolcini To: Dong Aisheng , Andi Shyti , Shawn Guo , Sascha Hauer , Pengutronix Kernel Team , Fabio Estevam Cc: Emanuele Ghidoli , linux-i2c@vger.kernel.org, imx@lists.linux.dev, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, Francesco Dolcini , Carlos Song , Primoz Fiser Subject: [PATCH v5 2/2] i2c: lpi2c: implement xfer_atomic callback Date: Fri, 18 Jul 2025 15:34:29 +0200 Message-Id: <20250718133429.67219-3-francesco@dolcini.it> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20250718133429.67219-1-francesco@dolcini.it> References: <20250718133429.67219-1-francesco@dolcini.it> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Emanuele Ghidoli Rework the read and write code paths in the driver to support operation in atomic contexts. To achieve this, the driver must not rely on IRQs or perform any scheduling, e.g., via a sleep or schedule routine. Implement atomic, sleep-free, and IRQ-less operation. This increases complexity but is necessary for atomic I2C transfers required by some hardware configurations, e.g., to trigger reboots on an external PMIC chip. Signed-off-by: Emanuele Ghidoli Signed-off-by: Francesco Dolcini Reviewed-by: Carlos Song Tested-by: Primoz Fiser --- v4 -> v5 - rebased due to commit 614b1c3cbfb0 ("i2c: use inclusive callbacks in stru= ct i2c_algorithm"). - add Tested-by: Primoz=20 v3 -> v4 - Split the patch into two parts: one for the readl_poll_timeout() and one = for the atomic implementation. - Change the readl_poll_timeout() delay to 0 us to do not change the behavi= or of the driver. - Use err variable consistently. v2 -> v3 - Closes: https://lore.kernel.org/oe-kbuild-all/202505130735.zh3WuTNu-lkp@i= ntel.com/. Using the return value of lpi2c_imx_read_msr_poll_timeout() to check for errors. v1 -> v2 - Rename READL_POLL_TIMEOUT to lpi2c_imx_read_msr_poll_timeout - Remove addr and timeout_us parameters from lpi2c_imx_read_msr_poll_timeou= t since they are used always with the same value - add r-b tag from Carlos Song --- drivers/i2c/busses/i2c-imx-lpi2c.c | 170 ++++++++++++++++++++++------- 1 file changed, 132 insertions(+), 38 deletions(-) diff --git a/drivers/i2c/busses/i2c-imx-lpi2c.c b/drivers/i2c/busses/i2c-im= x-lpi2c.c index 4a25c92adfc1..3ba7825f66ef 100644 --- a/drivers/i2c/busses/i2c-imx-lpi2c.c +++ b/drivers/i2c/busses/i2c-imx-lpi2c.c @@ -188,9 +188,11 @@ struct lpi2c_imx_struct { struct i2c_client *target; }; =20 -#define lpi2c_imx_read_msr_poll_timeout(val, cond) = \ +#define lpi2c_imx_read_msr_poll_timeout(atomic, val, cond) = \ + (atomic ? readl_poll_timeout_atomic(lpi2c_imx->base + LPI2C_MSR, val, \ + cond, 0, 500000) : \ readl_poll_timeout(lpi2c_imx->base + LPI2C_MSR, val, cond, \ - 0, 500000) + 0, 500000)) =20 static void lpi2c_imx_intctrl(struct lpi2c_imx_struct *lpi2c_imx, unsigned int enable) @@ -198,12 +200,12 @@ static void lpi2c_imx_intctrl(struct lpi2c_imx_struct= *lpi2c_imx, writel(enable, lpi2c_imx->base + LPI2C_MIER); } =20 -static int lpi2c_imx_bus_busy(struct lpi2c_imx_struct *lpi2c_imx) +static int lpi2c_imx_bus_busy(struct lpi2c_imx_struct *lpi2c_imx, bool ato= mic) { int err; unsigned int temp; =20 - err =3D lpi2c_imx_read_msr_poll_timeout(temp, + err =3D lpi2c_imx_read_msr_poll_timeout(atomic, temp, temp & (MSR_ALF | MSR_BBF | MSR_MBF)); =20 /* check for arbitration lost, clear if set */ @@ -248,7 +250,7 @@ static void lpi2c_imx_set_mode(struct lpi2c_imx_struct = *lpi2c_imx) } =20 static int lpi2c_imx_start(struct lpi2c_imx_struct *lpi2c_imx, - struct i2c_msg *msgs) + struct i2c_msg *msgs, bool atomic) { unsigned int temp; =20 @@ -260,17 +262,17 @@ static int lpi2c_imx_start(struct lpi2c_imx_struct *l= pi2c_imx, temp =3D i2c_8bit_addr_from_msg(msgs) | (GEN_START << 8); writel(temp, lpi2c_imx->base + LPI2C_MTDR); =20 - return lpi2c_imx_bus_busy(lpi2c_imx); + return lpi2c_imx_bus_busy(lpi2c_imx, atomic); } =20 -static void lpi2c_imx_stop(struct lpi2c_imx_struct *lpi2c_imx) +static void lpi2c_imx_stop(struct lpi2c_imx_struct *lpi2c_imx, bool atomic) { unsigned int temp; int err; =20 writel(GEN_STOP << 8, lpi2c_imx->base + LPI2C_MTDR); =20 - err =3D lpi2c_imx_read_msr_poll_timeout(temp, temp & MSR_SDF); + err =3D lpi2c_imx_read_msr_poll_timeout(atomic, temp, temp & MSR_SDF); =20 if (err) { dev_dbg(&lpi2c_imx->adapter.dev, "stop timeout\n"); @@ -390,12 +392,12 @@ static int lpi2c_imx_pio_msg_complete(struct lpi2c_im= x_struct *lpi2c_imx) return time_left ? 0 : -ETIMEDOUT; } =20 -static int lpi2c_imx_txfifo_empty(struct lpi2c_imx_struct *lpi2c_imx) +static int lpi2c_imx_txfifo_empty(struct lpi2c_imx_struct *lpi2c_imx, bool= atomic) { unsigned int temp; int err; =20 - err =3D lpi2c_imx_read_msr_poll_timeout(temp, + err =3D lpi2c_imx_read_msr_poll_timeout(atomic, temp, (temp & MSR_NDF) || !lpi2c_imx_txfifo_cnt(lpi2c_imx)); =20 if (temp & MSR_NDF) { @@ -432,7 +434,7 @@ static void lpi2c_imx_set_rx_watermark(struct lpi2c_imx= _struct *lpi2c_imx) writel(temp << 16, lpi2c_imx->base + LPI2C_MFCR); } =20 -static void lpi2c_imx_write_txfifo(struct lpi2c_imx_struct *lpi2c_imx) +static bool lpi2c_imx_write_txfifo(struct lpi2c_imx_struct *lpi2c_imx, boo= l atomic) { unsigned int data, txcnt; =20 @@ -447,13 +449,19 @@ static void lpi2c_imx_write_txfifo(struct lpi2c_imx_s= truct *lpi2c_imx) txcnt++; } =20 - if (lpi2c_imx->delivered < lpi2c_imx->msglen) - lpi2c_imx_intctrl(lpi2c_imx, MIER_TDIE | MIER_NDIE); - else + if (lpi2c_imx->delivered < lpi2c_imx->msglen) { + if (!atomic) + lpi2c_imx_intctrl(lpi2c_imx, MIER_TDIE | MIER_NDIE); + return false; + } + + if (!atomic) complete(&lpi2c_imx->complete); + + return true; } =20 -static void lpi2c_imx_read_rxfifo(struct lpi2c_imx_struct *lpi2c_imx) +static bool lpi2c_imx_read_rxfifo(struct lpi2c_imx_struct *lpi2c_imx, bool= atomic) { unsigned int blocklen, remaining; unsigned int temp, data; @@ -478,8 +486,9 @@ static void lpi2c_imx_read_rxfifo(struct lpi2c_imx_stru= ct *lpi2c_imx) remaining =3D lpi2c_imx->msglen - lpi2c_imx->delivered; =20 if (!remaining) { - complete(&lpi2c_imx->complete); - return; + if (!atomic) + complete(&lpi2c_imx->complete); + return true; } =20 /* not finished, still waiting for rx data */ @@ -497,7 +506,10 @@ static void lpi2c_imx_read_rxfifo(struct lpi2c_imx_str= uct *lpi2c_imx) writel(temp, lpi2c_imx->base + LPI2C_MTDR); } =20 - lpi2c_imx_intctrl(lpi2c_imx, MIER_RDIE); + if (!atomic) + lpi2c_imx_intctrl(lpi2c_imx, MIER_RDIE); + + return false; } =20 static void lpi2c_imx_write(struct lpi2c_imx_struct *lpi2c_imx, @@ -505,11 +517,29 @@ static void lpi2c_imx_write(struct lpi2c_imx_struct *= lpi2c_imx, { lpi2c_imx->tx_buf =3D msgs->buf; lpi2c_imx_set_tx_watermark(lpi2c_imx); - lpi2c_imx_write_txfifo(lpi2c_imx); + lpi2c_imx_write_txfifo(lpi2c_imx, false); } =20 -static void lpi2c_imx_read(struct lpi2c_imx_struct *lpi2c_imx, - struct i2c_msg *msgs) +static int lpi2c_imx_write_atomic(struct lpi2c_imx_struct *lpi2c_imx, + struct i2c_msg *msgs) +{ + u32 temp; + int err; + + lpi2c_imx->tx_buf =3D msgs->buf; + + err =3D lpi2c_imx_read_msr_poll_timeout(true, temp, + (temp & MSR_NDF) || + lpi2c_imx_write_txfifo(lpi2c_imx, true)); + + if (temp & MSR_NDF) + return -EIO; + + return err; +} + +static void lpi2c_imx_read_init(struct lpi2c_imx_struct *lpi2c_imx, + struct i2c_msg *msgs) { unsigned int temp; =20 @@ -520,8 +550,43 @@ static void lpi2c_imx_read(struct lpi2c_imx_struct *lp= i2c_imx, temp =3D msgs->len > CHUNK_DATA ? CHUNK_DATA - 1 : msgs->len - 1; temp |=3D (RECV_DATA << 8); writel(temp, lpi2c_imx->base + LPI2C_MTDR); +} =20 - lpi2c_imx_intctrl(lpi2c_imx, MIER_RDIE | MIER_NDIE); +static bool lpi2c_imx_read_chunk_atomic(struct lpi2c_imx_struct *lpi2c_imx) +{ + u32 rxcnt; + + rxcnt =3D (readl(lpi2c_imx->base + LPI2C_MFSR) >> 16) & 0xFF; + if (!rxcnt) + return false; + + if (!lpi2c_imx_read_rxfifo(lpi2c_imx, true)) + return false; + + return true; +} + +static int lpi2c_imx_read_atomic(struct lpi2c_imx_struct *lpi2c_imx, + struct i2c_msg *msgs) +{ + u32 temp; + int tmo_us; + + tmo_us =3D 1000000; + do { + if (lpi2c_imx_read_chunk_atomic(lpi2c_imx)) + return 0; + + temp =3D readl(lpi2c_imx->base + LPI2C_MSR); + + if (temp & MSR_NDF) + return -EIO; + + udelay(100); + tmo_us -=3D 100; + } while (tmo_us > 0); + + return -ETIMEDOUT; } =20 static bool is_use_dma(struct lpi2c_imx_struct *lpi2c_imx, struct i2c_msg = *msg) @@ -541,14 +606,27 @@ static int lpi2c_imx_pio_xfer(struct lpi2c_imx_struct= *lpi2c_imx, { reinit_completion(&lpi2c_imx->complete); =20 - if (msg->flags & I2C_M_RD) - lpi2c_imx_read(lpi2c_imx, msg); - else + if (msg->flags & I2C_M_RD) { + lpi2c_imx_read_init(lpi2c_imx, msg); + lpi2c_imx_intctrl(lpi2c_imx, MIER_RDIE | MIER_NDIE); + } else { lpi2c_imx_write(lpi2c_imx, msg); + } =20 return lpi2c_imx_pio_msg_complete(lpi2c_imx); } =20 +static int lpi2c_imx_pio_xfer_atomic(struct lpi2c_imx_struct *lpi2c_imx, + struct i2c_msg *msg) +{ + if (msg->flags & I2C_M_RD) { + lpi2c_imx_read_init(lpi2c_imx, msg); + return lpi2c_imx_read_atomic(lpi2c_imx, msg); + } + + return lpi2c_imx_write_atomic(lpi2c_imx, msg); +} + static int lpi2c_imx_dma_timeout_calculate(struct lpi2c_imx_struct *lpi2c_= imx) { unsigned long time =3D 0; @@ -943,8 +1021,8 @@ static int lpi2c_imx_dma_xfer(struct lpi2c_imx_struct = *lpi2c_imx, return ret; } =20 -static int lpi2c_imx_xfer(struct i2c_adapter *adapter, - struct i2c_msg *msgs, int num) +static int lpi2c_imx_xfer_common(struct i2c_adapter *adapter, + struct i2c_msg *msgs, int num, bool atomic) { struct lpi2c_imx_struct *lpi2c_imx =3D i2c_get_adapdata(adapter); unsigned int temp; @@ -955,7 +1033,7 @@ static int lpi2c_imx_xfer(struct i2c_adapter *adapter, return result; =20 for (i =3D 0; i < num; i++) { - result =3D lpi2c_imx_start(lpi2c_imx, &msgs[i]); + result =3D lpi2c_imx_start(lpi2c_imx, &msgs[i], atomic); if (result) goto disable; =20 @@ -967,28 +1045,33 @@ static int lpi2c_imx_xfer(struct i2c_adapter *adapte= r, lpi2c_imx->tx_buf =3D NULL; lpi2c_imx->delivered =3D 0; lpi2c_imx->msglen =3D msgs[i].len; - init_completion(&lpi2c_imx->complete); =20 - if (is_use_dma(lpi2c_imx, &msgs[i])) { - result =3D lpi2c_imx_dma_xfer(lpi2c_imx, &msgs[i]); - if (result && lpi2c_imx->dma->using_pio_mode) - result =3D lpi2c_imx_pio_xfer(lpi2c_imx, &msgs[i]); + if (atomic) { + result =3D lpi2c_imx_pio_xfer_atomic(lpi2c_imx, &msgs[i]); } else { - result =3D lpi2c_imx_pio_xfer(lpi2c_imx, &msgs[i]); + init_completion(&lpi2c_imx->complete); + + if (is_use_dma(lpi2c_imx, &msgs[i])) { + result =3D lpi2c_imx_dma_xfer(lpi2c_imx, &msgs[i]); + if (result && lpi2c_imx->dma->using_pio_mode) + result =3D lpi2c_imx_pio_xfer(lpi2c_imx, &msgs[i]); + } else { + result =3D lpi2c_imx_pio_xfer(lpi2c_imx, &msgs[i]); + } } =20 if (result) goto stop; =20 if (!(msgs[i].flags & I2C_M_RD)) { - result =3D lpi2c_imx_txfifo_empty(lpi2c_imx); + result =3D lpi2c_imx_txfifo_empty(lpi2c_imx, atomic); if (result) goto stop; } } =20 stop: - lpi2c_imx_stop(lpi2c_imx); + lpi2c_imx_stop(lpi2c_imx, atomic); =20 temp =3D readl(lpi2c_imx->base + LPI2C_MSR); if ((temp & MSR_NDF) && !result) @@ -1004,6 +1087,16 @@ static int lpi2c_imx_xfer(struct i2c_adapter *adapte= r, return (result < 0) ? result : num; } =20 +static int lpi2c_imx_xfer(struct i2c_adapter *adapter, struct i2c_msg *msg= s, int num) +{ + return lpi2c_imx_xfer_common(adapter, msgs, num, false); +} + +static int lpi2c_imx_xfer_atomic(struct i2c_adapter *adapter, struct i2c_m= sg *msgs, int num) +{ + return lpi2c_imx_xfer_common(adapter, msgs, num, true); +} + static irqreturn_t lpi2c_imx_target_isr(struct lpi2c_imx_struct *lpi2c_imx, u32 ssr, u32 sier_filter) { @@ -1066,9 +1159,9 @@ static irqreturn_t lpi2c_imx_master_isr(struct lpi2c_= imx_struct *lpi2c_imx) if (temp & MSR_NDF) complete(&lpi2c_imx->complete); else if (temp & MSR_RDF) - lpi2c_imx_read_rxfifo(lpi2c_imx); + lpi2c_imx_read_rxfifo(lpi2c_imx, false); else if (temp & MSR_TDF) - lpi2c_imx_write_txfifo(lpi2c_imx); + lpi2c_imx_write_txfifo(lpi2c_imx, false); =20 return IRQ_HANDLED; } @@ -1265,6 +1358,7 @@ static u32 lpi2c_imx_func(struct i2c_adapter *adapter) =20 static const struct i2c_algorithm lpi2c_imx_algo =3D { .xfer =3D lpi2c_imx_xfer, + .xfer_atomic =3D lpi2c_imx_xfer_atomic, .functionality =3D lpi2c_imx_func, .reg_target =3D lpi2c_imx_register_target, .unreg_target =3D lpi2c_imx_unregister_target, --=20 2.39.5