From patchwork Mon Aug 3 20:28:07 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Cristian Ciocaltea X-Patchwork-Id: 11698781 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 303E9138A for ; Mon, 3 Aug 2020 20:28:31 +0000 (UTC) Received: from merlin.infradead.org (merlin.infradead.org [205.233.59.134]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 5ECC92086A for ; Mon, 3 Aug 2020 20:28:31 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="2LsXJNjv"; dkim=fail reason="signature verification failed" (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="pM9LeVfh" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 5ECC92086A Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=merlin.20170209; h=Sender:Content-Transfer-Encoding: Content-Type:Cc:List-Subscribe:List-Help:List-Post:List-Archive: List-Unsubscribe:List-Id:MIME-Version:Message-Id:Date:Subject:To:From: Reply-To:Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender :Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To:References:List-Owner; bh=jk/qfFbTuqAO9DzU/qGxNgKhjeJv4Zw/M7+XafqMnxA=; b=2LsXJNjvHqz3iTNUUuayh/ms2d O63x0fel9pGIHseNPSOAlJMh+lkVnV2E2UF7JnlFTChjk8NNICN+L1IcFkN9slB2XmjNzoTTJ0XRi yuUNkLad3fRbLcC97KLwJ/MjIrSvG6R6QU1QxPDzW6tR84kXZmAMFt328FS0Jm5oKuGD2EWh6Ll9e TtWDg+GeqHgkMzA155b6wnrQ2AnKsigJQnKbpnjXsOR8tqVKuZEAAj7gxnvDUQ3al2Z69AqlJ020n EYnJXAcBoUkLSbQZU0uOb6DbNhoSwTsS+5oCdZTxhu09lOqCAwzpuJGEVrW4h6Nb6RT6NqRwurlRi 9bsXSuDw==; Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.92.3 #3 (Red Hat Linux)) id 1k2h4A-0005cw-53; Mon, 03 Aug 2020 20:28:14 +0000 Received: from mail-ej1-x644.google.com ([2a00:1450:4864:20::644]) by merlin.infradead.org with esmtps (Exim 4.92.3 #3 (Red Hat Linux)) id 1k2h47-0005c5-Tb; Mon, 03 Aug 2020 20:28:12 +0000 Received: by mail-ej1-x644.google.com with SMTP id o23so12562127ejr.1; Mon, 03 Aug 2020 13:28:10 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=2aSWORY9oAWNRE8xXVrMliziKzrNkAApqFQEbxBMDms=; b=pM9LeVfhQspLoy8T+wOwOh66v+8IfX1DjYiw7oYHoUHOfpLmUqEROKQMN0qSBrrgNB k7JA/Lkq1RpahqNNb/EasSQbVBBs2sy2iGGaV4VfFqizZIsL2pMhv0TgLKUi94/jWE6J /lu5J/Lwl8ThqFj9bHaa4qEWvoqbEaBjyeuJ5IneNrOPJxDbOhe4OtDXsxJKL/gTIS5Z Qcp5f4vMvo2PXGB0zt1ziOsRyhRya9f7VG/wRRDZtS+g1uG5EPGtQ2n6sH8MDPtLUWiz B1kfbiH41tLxmjJ9+ok10ANWmslT6QHqy1pPLmMY9rqzCnQhd4uK2VRD+Thy1OOHzWZy T8Zg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=2aSWORY9oAWNRE8xXVrMliziKzrNkAApqFQEbxBMDms=; b=EJw+OVYKij7YWuqoQYU1VA6yApfPEA/XyjAOWmhx+4EumbgKK9HH0ez50F4rQbYbAV vIONhNExMpD9GjJLNkvGceKTB+cz9YnAx+IYamQ2RcQr9Bi/ybDL3iWbK2tlclFwe2K6 pNU77f9JH4jv+42lvq8R7Yz8Aw90z4Vm+b64HKNE1wvIOqPUOUprNAoCLD5ewvnLNlgc CpN0Mko+jnROxcS21Xjce/JP/1FZmCpNrlNKRjJZW3JPMWepuDhCRDFR73p0M3LTHmkw eAtcDTSSY3ChdXH0c8OlfSC9rASuqUSYNQxw5w2QwC0p5njUWFOBF/pKFcW/Rd495StO ypyg== X-Gm-Message-State: AOAM533qgcfWTMLb4LEGYMIQL8K7PoWUsVED6rmtGc1PIoUrfo1c5erl IlL3/G9xUleECpFDjHqolgo= X-Google-Smtp-Source: ABdhPJzD18u4BB+gHJr0fDYI7QdVbV6wGliJO+uub6j9vI7d3zFY+jdf2zXz/1CS6nN+PAuv1J5cQg== X-Received: by 2002:a17:906:6406:: with SMTP id d6mr17644049ejm.30.1596486489794; Mon, 03 Aug 2020 13:28:09 -0700 (PDT) Received: from localhost.localdomain ([86.121.43.21]) by smtp.gmail.com with ESMTPSA id v2sm17405859edb.95.2020.08.03.13.28.08 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 03 Aug 2020 13:28:09 -0700 (PDT) From: Cristian Ciocaltea To: =?utf-8?q?Andreas_F=C3=A4rber?= , Manivannan Sadhasivam Subject: [PATCH 1/1] i2c: busses: Add support for atomic transfers in Actions Semi Owl driver Date: Mon, 3 Aug 2020 23:28:07 +0300 Message-Id: X-Mailer: git-send-email 2.28.0 MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20200803_162811_972708_2640792F X-CRM114-Status: GOOD ( 22.60 ) X-Spam-Score: -0.2 (/) X-Spam-Report: SpamAssassin version 3.4.4 on merlin.infradead.org summary: Content analysis details: (-0.2 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 RCVD_IN_DNSWL_NONE RBL: Sender listed at https://www.dnswl.org/, no trust [2a00:1450:4864:20:0:0:0:644 listed in] [list.dnswl.org] 0.0 SPF_HELO_NONE SPF: HELO does not publish an SPF Record -0.0 SPF_PASS SPF: sender matches SPF record 0.0 FREEMAIL_FROM Sender email is commonly abused enduser mail provider [cristian.ciocaltea[at]gmail.com] -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.1 DKIM_VALID_EF Message has a valid DKIM or DK signature from envelope-from domain X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: linux-actions@lists.infradead.org, linux-i2c@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org Atomic transfers are required to properly power off a machine through an I2C controlled PMIC, such as the Actions Semi ATC260x series. System shutdown may happen with interrupts being disabled and, as a consequence, the kernel may hang if the driver does not support atomic transfers. This functionality is essentially implemented by polling the FIFO Status register until either Command Execute Completed or NACK Error bits are set. Signed-off-by: Cristian Ciocaltea --- drivers/i2c/busses/i2c-owl.c | 80 ++++++++++++++++++++++++++---------- 1 file changed, 59 insertions(+), 21 deletions(-) diff --git a/drivers/i2c/busses/i2c-owl.c b/drivers/i2c/busses/i2c-owl.c index 672f1f239bd6..90dd2e814313 100644 --- a/drivers/i2c/busses/i2c-owl.c +++ b/drivers/i2c/busses/i2c-owl.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -76,6 +77,7 @@ #define OWL_I2C_FIFOCTL_TFR BIT(2) /* I2Cc_FIFOSTAT Bit Mask */ +#define OWL_I2C_FIFOSTAT_CECB BIT(0) #define OWL_I2C_FIFOSTAT_RNB BIT(1) #define OWL_I2C_FIFOSTAT_RFE BIT(2) #define OWL_I2C_FIFOSTAT_TFF BIT(5) @@ -83,7 +85,8 @@ #define OWL_I2C_FIFOSTAT_RFD GENMASK(15, 8) /* I2C bus timeout */ -#define OWL_I2C_TIMEOUT msecs_to_jiffies(4 * 1000) +#define OWL_I2C_TIMEOUT_MS (4 * 1000) +#define OWL_I2C_TIMEOUT msecs_to_jiffies(OWL_I2C_TIMEOUT_MS) #define OWL_I2C_MAX_RETRIES 50 @@ -161,29 +164,25 @@ static void owl_i2c_set_freq(struct owl_i2c_dev *i2c_dev) writel(OWL_I2C_DIV_FACTOR(val), i2c_dev->base + OWL_I2C_REG_CLKDIV); } -static irqreturn_t owl_i2c_interrupt(int irq, void *_dev) +static int owl_i2c_xfer_data(struct owl_i2c_dev *i2c_dev) { - struct owl_i2c_dev *i2c_dev = _dev; struct i2c_msg *msg = i2c_dev->msg; - unsigned long flags; unsigned int stat, fifostat; - spin_lock_irqsave(&i2c_dev->lock, flags); - i2c_dev->err = 0; /* Handle NACK from slave */ fifostat = readl(i2c_dev->base + OWL_I2C_REG_FIFOSTAT); if (fifostat & OWL_I2C_FIFOSTAT_RNB) { i2c_dev->err = -ENXIO; - goto stop; + return 1; } /* Handle bus error */ stat = readl(i2c_dev->base + OWL_I2C_REG_STAT); if (stat & OWL_I2C_STAT_BEB) { i2c_dev->err = -EIO; - goto stop; + return 1; } /* Handle FIFO read */ @@ -196,18 +195,30 @@ static irqreturn_t owl_i2c_interrupt(int irq, void *_dev) } else { /* Handle the remaining bytes which were not sent */ while (!(readl(i2c_dev->base + OWL_I2C_REG_FIFOSTAT) & - OWL_I2C_FIFOSTAT_TFF) && i2c_dev->msg_ptr < msg->len) { + OWL_I2C_FIFOSTAT_TFF) && i2c_dev->msg_ptr < msg->len) { writel(msg->buf[i2c_dev->msg_ptr++], i2c_dev->base + OWL_I2C_REG_TXDAT); } } -stop: + return 0; +} + +static irqreturn_t owl_i2c_interrupt(int irq, void *_dev) +{ + struct owl_i2c_dev *i2c_dev = _dev; + unsigned long flags; + + spin_lock_irqsave(&i2c_dev->lock, flags); + + owl_i2c_xfer_data(i2c_dev); + /* Clear pending interrupts */ owl_i2c_update_reg(i2c_dev->base + OWL_I2C_REG_STAT, OWL_I2C_STAT_IRQP, true); complete_all(&i2c_dev->msg_complete); + spin_unlock_irqrestore(&i2c_dev->lock, flags); return IRQ_HANDLED; @@ -235,8 +246,8 @@ static int owl_i2c_check_bus_busy(struct i2c_adapter *adap) return 0; } -static int owl_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, - int num) +static int owl_i2c_xfer_common(struct i2c_adapter *adap, struct i2c_msg *msgs, + int num, bool atomic) { struct owl_i2c_dev *i2c_dev = i2c_get_adapdata(adap); struct i2c_msg *msg; @@ -280,11 +291,12 @@ static int owl_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, goto err_exit; } - reinit_completion(&i2c_dev->msg_complete); + if (!atomic) + reinit_completion(&i2c_dev->msg_complete); - /* Enable I2C controller interrupt */ + /* Enable/disable I2C controller interrupt */ owl_i2c_update_reg(i2c_dev->base + OWL_I2C_REG_CTL, - OWL_I2C_CTL_IRQE, true); + OWL_I2C_CTL_IRQE, !atomic); /* * Select: FIFO enable, Master mode, Stop enable, Data count enable, @@ -352,20 +364,33 @@ static int owl_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, spin_unlock_irqrestore(&i2c_dev->lock, flags); - time_left = wait_for_completion_timeout(&i2c_dev->msg_complete, - adap->timeout); + if (atomic) { + /* Wait for Command Execute Completed or NACK Error bits */ + ret = readl_poll_timeout_atomic(i2c_dev->base + OWL_I2C_REG_FIFOSTAT, + val, val & (OWL_I2C_FIFOSTAT_CECB | + OWL_I2C_FIFOSTAT_RNB), + 10, OWL_I2C_TIMEOUT_MS * 1000); + } else { + time_left = wait_for_completion_timeout(&i2c_dev->msg_complete, + adap->timeout); + if (!time_left) + ret = -ETIMEDOUT; + } spin_lock_irqsave(&i2c_dev->lock, flags); - if (time_left == 0) { + + if (ret) { dev_err(&adap->dev, "Transaction timed out\n"); /* Send stop condition and release the bus */ owl_i2c_update_reg(i2c_dev->base + OWL_I2C_REG_CTL, OWL_I2C_CTL_GBCC_STOP | OWL_I2C_CTL_RB, true); - ret = -ETIMEDOUT; goto err_exit; } + if (atomic) + owl_i2c_xfer_data(i2c_dev); + ret = i2c_dev->err < 0 ? i2c_dev->err : num; err_exit: @@ -379,9 +404,22 @@ static int owl_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, return ret; } +static int owl_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, + int num) +{ + return owl_i2c_xfer_common(adap, msgs, num, false); +} + +static int owl_i2c_xfer_atomic(struct i2c_adapter *adap, + struct i2c_msg *msgs, int num) +{ + return owl_i2c_xfer_common(adap, msgs, num, true); +} + static const struct i2c_algorithm owl_i2c_algorithm = { - .master_xfer = owl_i2c_master_xfer, - .functionality = owl_i2c_func, + .master_xfer = owl_i2c_xfer, + .master_xfer_atomic = owl_i2c_xfer_atomic, + .functionality = owl_i2c_func, }; static const struct i2c_adapter_quirks owl_i2c_quirks = {