devicetree.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Karthik Ramasubramanian <kramasub@codeaurora.org>
To: Bjorn Andersson <bjorn.andersson@linaro.org>
Cc: corbet@lwn.net, andy.gross@linaro.org, david.brown@linaro.org,
	robh+dt@kernel.org, mark.rutland@arm.com, wsa@the-dreams.de,
	gregkh@linuxfoundation.org, linux-doc@vger.kernel.org,
	linux-arm-msm@vger.kernel.org, devicetree@vger.kernel.org,
	linux-i2c@vger.kernel.org, linux-serial@vger.kernel.org,
	jslaby@suse.com, Sagar Dharia <sdharia@codeaurora.org>,
	Girish Mahadevan <girishm@codeaurora.org>
Subject: Re: [PATCH v2 5/7] i2c: i2c-qcom-geni: Add bus driver for the Qualcomm GENI I2C controller
Date: Tue, 27 Feb 2018 06:16:06 -0700	[thread overview]
Message-ID: <4ff97d07-c28b-0bbb-5891-e696bce6a3c9@codeaurora.org> (raw)
In-Reply-To: <20180118052353.GA12583@builder>



On 1/17/2018 10:23 PM, Bjorn Andersson wrote:
> On Fri 12 Jan 17:05 PST 2018, Karthikeyan Ramasubramanian wrote:
> 
>> This bus driver supports the GENI based i2c hardware controller in the
>> Qualcomm SOCs. The Qualcomm Generic Interface (GENI) is a programmable
>> module supporting a wide range of serial interfaces including I2C. The
>> driver supports FIFO mode and DMA mode of transfer and switches modes
>> dynamically depending on the size of the transfer.
>>
>> Signed-off-by: Karthikeyan Ramasubramanian <kramasub@codeaurora.org>
>> Signed-off-by: Sagar Dharia <sdharia@codeaurora.org>
>> Signed-off-by: Girish Mahadevan <girishm@codeaurora.org>
>> ---
>>   drivers/i2c/busses/Kconfig         |  10 +
>>   drivers/i2c/busses/Makefile        |   1 +
>>   drivers/i2c/busses/i2c-qcom-geni.c | 656 +++++++++++++++++++++++++++++++++++++
>>   3 files changed, 667 insertions(+)
>>   create mode 100644 drivers/i2c/busses/i2c-qcom-geni.c
>>
>> diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
>> index 009345d..caef309 100644
>> --- a/drivers/i2c/busses/Kconfig
>> +++ b/drivers/i2c/busses/Kconfig
>> @@ -838,6 +838,16 @@ config I2C_PXA_SLAVE
>>   	  is necessary for systems where the PXA may be a target on the
>>   	  I2C bus.
>>   
>> +config I2C_QCOM_GENI
>> +	tristate "Qualcomm Technologies Inc.'s GENI based I2C controller"
>> +	depends on ARCH_QCOM
> 
> Just depend on the GENI wrapper as well.
Ok.
> 
>> +	help
>> +	  If you say yes to this option, support will be included for the
>> +	  built-in I2C interface on the Qualcomm Technologies Inc.'s SoCs.
>> +
>> +	  This driver can also be built as a module.  If so, the module
>> +	  will be called i2c-qcom-geni.
>> +
>>   config I2C_QUP
>>   	tristate "Qualcomm QUP based I2C controller"
>>   	depends on ARCH_QCOM
>> diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
>> index 2ce8576..201fce1 100644
>> --- a/drivers/i2c/busses/Makefile
>> +++ b/drivers/i2c/busses/Makefile
>> @@ -84,6 +84,7 @@ obj-$(CONFIG_I2C_PNX)		+= i2c-pnx.o
>>   obj-$(CONFIG_I2C_PUV3)		+= i2c-puv3.o
>>   obj-$(CONFIG_I2C_PXA)		+= i2c-pxa.o
>>   obj-$(CONFIG_I2C_PXA_PCI)	+= i2c-pxa-pci.o
>> +obj-$(CONFIG_I2C_QCOM_GENI)	+= i2c-qcom-geni.o
>>   obj-$(CONFIG_I2C_QUP)		+= i2c-qup.o
>>   obj-$(CONFIG_I2C_RIIC)		+= i2c-riic.o
>>   obj-$(CONFIG_I2C_RK3X)		+= i2c-rk3x.o
>> diff --git a/drivers/i2c/busses/i2c-qcom-geni.c b/drivers/i2c/busses/i2c-qcom-geni.c
>> new file mode 100644
>> index 0000000..59ad4da
>> --- /dev/null
>> +++ b/drivers/i2c/busses/i2c-qcom-geni.c
>> @@ -0,0 +1,656 @@
>> +/*
>> + * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 and
>> + * only version 2 as published by the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + *
>> + */
> 
> Use SPDX license header.
Ok.
> 
>> +
>> +#include <linux/clk.h>
>> +#include <linux/delay.h>
> 
> Unused?
Ok, I will remove unused header files.
> 
>> +#include <linux/err.h>
>> +#include <linux/i2c.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/io.h>
>> +#include <linux/module.h>
>> +#include <linux/of.h>
>> +#include <linux/of_platform.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/pm_runtime.h>
>> +#include <linux/dma-mapping.h>
>> +#include <linux/qcom-geni-se.h>
>> +
>> +#define SE_I2C_TX_TRANS_LEN		(0x26C)
> 
> Drop the parenthesis, when not needed.
Ok.
> 
>> +#define SE_I2C_RX_TRANS_LEN		(0x270)
>> +#define SE_I2C_SCL_COUNTERS		(0x278)
>> +#define SE_GENI_IOS			(0x908)
>> +
>> +#define SE_I2C_ERR  (M_CMD_OVERRUN_EN | M_ILLEGAL_CMD_EN | M_CMD_FAILURE_EN |\
>> +			M_GP_IRQ_1_EN | M_GP_IRQ_3_EN | M_GP_IRQ_4_EN)
>> +#define SE_I2C_ABORT (1U << 1)
>> +/* M_CMD OP codes for I2C */
>> +#define I2C_WRITE		(0x1)
>> +#define I2C_READ		(0x2)
>> +#define I2C_WRITE_READ		(0x3)
>> +#define I2C_ADDR_ONLY		(0x4)
>> +#define I2C_BUS_CLEAR		(0x6)
>> +#define I2C_STOP_ON_BUS		(0x7)
>> +/* M_CMD params for I2C */
>> +#define PRE_CMD_DELAY		(BIT(0))
>> +#define TIMESTAMP_BEFORE	(BIT(1))
>> +#define STOP_STRETCH		(BIT(2))
>> +#define TIMESTAMP_AFTER		(BIT(3))
>> +#define POST_COMMAND_DELAY	(BIT(4))
>> +#define IGNORE_ADD_NACK		(BIT(6))
>> +#define READ_FINISHED_WITH_ACK	(BIT(7))
>> +#define BYPASS_ADDR_PHASE	(BIT(8))
>> +#define SLV_ADDR_MSK		(GENMASK(15, 9))
>> +#define SLV_ADDR_SHFT		(9)
>> +
>> +#define I2C_CORE2X_VOTE		(10000)
>> +#define GP_IRQ0			0
>> +#define GP_IRQ1			1
>> +#define GP_IRQ2			2
>> +#define GP_IRQ3			3
>> +#define GP_IRQ4			4
>> +#define GP_IRQ5			5
>> +#define GENI_OVERRUN		6
>> +#define GENI_ILLEGAL_CMD	7
>> +#define GENI_ABORT_DONE		8
>> +#define GENI_TIMEOUT		9
>> +
>> +#define I2C_NACK		GP_IRQ1
>> +#define I2C_BUS_PROTO		GP_IRQ3
>> +#define I2C_ARB_LOST		GP_IRQ4
>> +#define DM_I2C_CB_ERR		((BIT(GP_IRQ1) | BIT(GP_IRQ3) | BIT(GP_IRQ4)) \
>> +									<< 5)
>> +
>> +#define I2C_AUTO_SUSPEND_DELAY	250
>> +#define KHz(freq)		(1000 * freq)
>> +
>> +struct geni_i2c_dev {
>> +	struct device *dev;
>> +	void __iomem *base;
>> +	unsigned int tx_wm;
>> +	int irq;
>> +	int err;
>> +	struct i2c_adapter adap;
>> +	struct completion xfer;
> 
> How about naming this "done" or something like that, gi2c->xfer doesn't
> really give the sense of being a "we're done with the operation"-event.
Ok.
> 
>> +	struct i2c_msg *cur;
>> +	struct geni_se_rsc i2c_rsc;
>> +	int cur_wr;
>> +	int cur_rd;
>> +	struct device *wrapper_dev;
> 
> This is already availabe in i2c_rsc, and in particular if you pass the
> i2c_rsc down to the wrapper in the 2 cases you use the wrapper_dev you
> don't need this duplication.
Ok.
> 
>> +	u32 clk_freq_out;
>> +	int clk_fld_idx;
> 
> Keep track of the const struct geni_i2c_clk_fld * here instead.
Ok.
> 
>> +};
>> +
>> +struct geni_i2c_err_log {
>> +	int err;
>> +	const char *msg;
>> +};
>> +
>> +static struct geni_i2c_err_log gi2c_log[] = {
>> +	[GP_IRQ0] = {-EINVAL, "Unknown I2C err GP_IRQ0"},
>> +	[I2C_NACK] = {-ENOTCONN,
>> +			"NACK: slv unresponsive, check its power/reset-ln"},
> 
> Break the 80-char rule, to improve readability.
Ok.
> 
>> +	[GP_IRQ2] = {-EINVAL, "Unknown I2C err GP IRQ2"},
>> +	[I2C_BUS_PROTO] = {-EPROTO,
>> +				"Bus proto err, noisy/unepxected start/stop"},
>> +	[I2C_ARB_LOST] = {-EBUSY,
>> +				"Bus arbitration lost, clock line undriveable"},
>> +	[GP_IRQ5] = {-EINVAL, "Unknown I2C err GP IRQ5"},
>> +	[GENI_OVERRUN] = {-EIO, "Cmd overrun, check GENI cmd-state machine"},
>> +	[GENI_ILLEGAL_CMD] = {-EILSEQ,
>> +				"Illegal cmd, check GENI cmd-state machine"},
>> +	[GENI_ABORT_DONE] = {-ETIMEDOUT, "Abort after timeout successful"},
>> +	[GENI_TIMEOUT] = {-ETIMEDOUT, "I2C TXN timed out"},
>> +};
>> +
>> +struct geni_i2c_clk_fld {
>> +	u32	clk_freq_out;
>> +	u8	clk_div;
>> +	u8	t_high;
>> +	u8	t_low;
>> +	u8	t_cycle;
>> +};
>> +
>> +static struct geni_i2c_clk_fld geni_i2c_clk_map[] = {
> 
> const
Ok.
> 
>> +	{KHz(100), 7, 10, 11, 26},
>> +	{KHz(400), 2,  5, 12, 24},
>> +	{KHz(1000), 1, 3,  9, 18},
>> +};
>> +
>> +static int geni_i2c_clk_map_idx(struct geni_i2c_dev *gi2c)
>> +{
>> +	int i;
>> +	int ret = 0;
>> +	bool clk_map_present = false;
>> +	struct geni_i2c_clk_fld *itr = geni_i2c_clk_map;
>> +
>> +	for (i = 0; i < ARRAY_SIZE(geni_i2c_clk_map); i++, itr++) {
>> +		if (itr->clk_freq_out == gi2c->clk_freq_out) {
>> +			clk_map_present = true;
>> +			break;
> 
> Make this:
> 			gi2c->clk_fld = geni_i2c_clk_map + i;
> 			return 0;
Ok.
> 
>> +		}
>> +	}
>> +
> 
> ...then you can drop ret and clk_map_present and just return -EINVAL
> here.
Ok.
> 
>> +	if (clk_map_present)
>> +		gi2c->clk_fld_idx = i;
>> +	else
>> +		ret = -EINVAL;
>> +
>> +	return ret;
>> +}
>> +
>> +static inline void qcom_geni_i2c_conf(struct geni_i2c_dev *gi2c, int dfs)
> 
> Drop the "inline", if it makes sense the compiler will inline it, if not
> it knows better than we do.
> 
> dfs is always 0, so drop this parameter and hard code the value below.
Ok.
> 
>> +{
>> +	struct geni_i2c_clk_fld *itr = geni_i2c_clk_map + gi2c->clk_fld_idx;
>> +
>> +	geni_write_reg(dfs, gi2c->base, SE_GENI_CLK_SEL);
>> +
>> +	geni_write_reg((itr->clk_div << 4) | 1, gi2c->base, GENI_SER_M_CLK_CFG);
>> +	geni_write_reg(((itr->t_high << 20) | (itr->t_low << 10) |
>> +			itr->t_cycle), gi2c->base, SE_I2C_SCL_COUNTERS);
>> +
>> +	/* Ensure Clk config completes before return */
> 
> That's not what "mb" does, it ensures that later memory operations
> aren't reordered beyond the barrier.
Our intention also is for ordering purposes so that any later register 
writes/reads does not get re-ordered until the writes to clock 
configuration register is complete. I will update the comment.
> 
>> +	mb();
>> +}
>> +
>> +static void geni_i2c_err(struct geni_i2c_dev *gi2c, int err)
> 
> Looking at the code in this function it's just a bunch of logic to print
> various debug messages...except for the last line that uses the gi2c_log
> lookup table to convert the error to a errno. Sneaky...and not very
> nice.
I will update this function so that it is more obvious.
> 
>> +{
>> +	u32 m_cmd = readl_relaxed(gi2c->base + SE_GENI_M_CMD0);
> 
> Unless you have a really good reason, just use readl/writel in favour of
> their _relaxed versions.
The goal is to maintain the order of execution to the Serial Engine 
register block. I believe _relaxed help enusre that order with a little 
less overhead compared to non_relaxed function. Please correct me otherwise.
> 
>> +	u32 m_stat = readl_relaxed(gi2c->base + SE_GENI_M_IRQ_STATUS);
>> +	u32 geni_s = readl_relaxed(gi2c->base + SE_GENI_STATUS);
>> +	u32 geni_ios = readl_relaxed(gi2c->base + SE_GENI_IOS);
>> +	u32 dma = readl_relaxed(gi2c->base + SE_GENI_DMA_MODE_EN);
>> +	u32 rx_st, tx_st;
>> +
>> +	if (gi2c->cur)
>> +		dev_dbg(gi2c->dev, "len:%d, slv-addr:0x%x, RD/WR:%d\n",
>> +			gi2c->cur->len, gi2c->cur->addr, gi2c->cur->flags);
>> +
>> +	if (err == I2C_NACK || err == GENI_ABORT_DONE) {
>> +		dev_dbg(gi2c->dev, "%s\n", gi2c_log[err].msg);
>> +		goto err_ret;
>> +	} else {
>> +		dev_err(gi2c->dev, "%s\n", gi2c_log[err].msg);
>> +	}
>> +
>> +	if (dma) {
>> +		rx_st = readl_relaxed(gi2c->base + SE_DMA_RX_IRQ_STAT);
>> +		tx_st = readl_relaxed(gi2c->base + SE_DMA_TX_IRQ_STAT);
>> +	} else {
>> +		rx_st = readl_relaxed(gi2c->base + SE_GENI_RX_FIFO_STATUS);
>> +		tx_st = readl_relaxed(gi2c->base + SE_GENI_TX_FIFO_STATUS);
>> +	}
>> +	dev_dbg(gi2c->dev, "DMA:%d tx_stat:0x%x, rx_stat:0x%x, irq-stat:0x%x\n",
>> +		dma, tx_st, rx_st, m_stat);
>> +	dev_dbg(gi2c->dev, "m_cmd:0x%x, geni_status:0x%x, geni_ios:0x%x\n",
>> +		m_cmd, geni_s, geni_ios);
>> +err_ret:
>> +	gi2c->err = gi2c_log[err].err;
>> +}
>> +
>> +static irqreturn_t geni_i2c_irq(int irq, void *dev)
>> +{
>> +	struct geni_i2c_dev *gi2c = dev;
>> +	int i, j;
>> +	u32 m_stat = readl_relaxed(gi2c->base + SE_GENI_M_IRQ_STATUS);
>> +	u32 rx_st = readl_relaxed(gi2c->base + SE_GENI_RX_FIFO_STATUS);
>> +	u32 dm_tx_st = readl_relaxed(gi2c->base + SE_DMA_TX_IRQ_STAT);
>> +	u32 dm_rx_st = readl_relaxed(gi2c->base + SE_DMA_RX_IRQ_STAT);
>> +	u32 dma = readl_relaxed(gi2c->base + SE_GENI_DMA_MODE_EN);
>> +	struct i2c_msg *cur = gi2c->cur;
>> +
>> +	if (!cur || (m_stat & M_CMD_FAILURE_EN) ||
>> +		    (dm_rx_st & (DM_I2C_CB_ERR)) ||
>> +		    (m_stat & M_CMD_ABORT_EN)) {
>> +
>> +		if (m_stat & M_GP_IRQ_1_EN)
>> +			geni_i2c_err(gi2c, I2C_NACK);
>> +		if (m_stat & M_GP_IRQ_3_EN)
>> +			geni_i2c_err(gi2c, I2C_BUS_PROTO);
>> +		if (m_stat & M_GP_IRQ_4_EN)
>> +			geni_i2c_err(gi2c, I2C_ARB_LOST);
>> +		if (m_stat & M_CMD_OVERRUN_EN)
>> +			geni_i2c_err(gi2c, GENI_OVERRUN);
>> +		if (m_stat & M_ILLEGAL_CMD_EN)
>> +			geni_i2c_err(gi2c, GENI_ILLEGAL_CMD);
>> +		if (m_stat & M_CMD_ABORT_EN)
>> +			geni_i2c_err(gi2c, GENI_ABORT_DONE);
>> +		if (m_stat & M_GP_IRQ_0_EN)
>> +			geni_i2c_err(gi2c, GP_IRQ0);
>> +
>> +		if (!dma)
>> +			writel_relaxed(0, (gi2c->base +
>> +					   SE_GENI_TX_WATERMARK_REG));
> 
> Drop the extra parenthesis. And using writel() instead keeps you under
> 80 chars.
Ok, to dropping the parenthesis.
> 
>> +		goto irqret;
>> +	}
>> +
>> +	if (dma) {
>> +		dev_dbg(gi2c->dev, "i2c dma tx:0x%x, dma rx:0x%x\n", dm_tx_st,
>> +			dm_rx_st);
>> +		goto irqret;
>> +	}
>> +
>> +	if (((m_stat & M_RX_FIFO_WATERMARK_EN) ||
>> +		(m_stat & M_RX_FIFO_LAST_EN)) && (cur->flags & I2C_M_RD)) {
> 
> Some of these parenthesis are unnecessary, but more importantly the way
> you wrap this line is misleading; the wrapping indicates that the two
> latter conditionals are related, but the parenthesis says the first two
> are.
> 
> The most significant part of this conditional is "is this a read
> operation", so put this first.
Ok.
> 
> 
>> +		u32 rxcnt = rx_st & RX_FIFO_WC_MSK;
>> +
>> +		for (j = 0; j < rxcnt; j++) {
>> +			u32 temp;
>> +			int p;
>> +
>> +			temp = readl_relaxed(gi2c->base + SE_GENI_RX_FIFOn);
>> +			for (i = gi2c->cur_rd, p = 0; (i < cur->len && p < 4);
>> +				i++, p++)
>> +				cur->buf[i] = (u8) ((temp >> (p * 8)) & 0xff);
>> +			gi2c->cur_rd = i;
> 
> The two-range for loop with a line wrap isn't very clean and the wrap
> calls for a set of braces - which will look ugly.
> 
> How about something like this instead?
> 
> 			p = 0;
> 			while (p < 4 && gi2c->cur_rd < cur->len) {
> 				cur->buf[gi2c->cur_rd++] = temp & 0xff;
> 				temp >>= 8;
> 				p++;
> 			}
Ok.
> 
>> +			if (gi2c->cur_rd == cur->len) {
>> +				dev_dbg(gi2c->dev, "FIFO i:%d,read 0x%x\n",
>> +					i, temp);
> 
> Who's the consumer of this debug line?
I will make the debug message more comprehensible, if that is what you mean.
> 
>> +				break;
>> +			}
>> +		}
>> +	} else if ((m_stat & M_TX_FIFO_WATERMARK_EN) &&
>> +					!(cur->flags & I2C_M_RD)) {
>> +		for (j = 0; j < gi2c->tx_wm; j++) {
>> +			u32 temp = 0;
>> +			int p;
>> +
>> +			for (i = gi2c->cur_wr, p = 0; (i < cur->len && p < 4);
>> +				i++, p++)
>> +				temp |= (((u32)(cur->buf[i]) << (p * 8)));
>> +			writel_relaxed(temp, gi2c->base + SE_GENI_TX_FIFOn);
> 
> Same concern as above.
Ok.
> 
>> +			gi2c->cur_wr = i;
>> +			dev_dbg(gi2c->dev, "FIFO i:%d,wrote 0x%x\n", i, temp);
>> +			if (gi2c->cur_wr == cur->len) {
>> +				dev_dbg(gi2c->dev, "FIFO i2c bytes done writing\n");
>> +				writel_relaxed(0,
>> +				(gi2c->base + SE_GENI_TX_WATERMARK_REG));
> 
> The line break here is bad.  checkpatch.pl --strict will help you find
> these.
Ok/
> 
> Also using writel() and dropping the parenthesis keeps you below 80
> chars.
Ok, to dropping the parenthesis.
> 
>> +				break;
>> +			}
>> +		}
>> +	}
>> +irqret:
>> +	if (m_stat)
>> +		writel_relaxed(m_stat, gi2c->base + SE_GENI_M_IRQ_CLEAR);
> 
> Is it okay for this to be re-ordered wrt above writes?
It is okay. The interrupt bitmask can be cleared anytime while handling 
the IRQ.
> 
>> +
>> +	if (dma) {
>> +		if (dm_tx_st)
>> +			writel_relaxed(dm_tx_st, gi2c->base +
>> +				       SE_DMA_TX_IRQ_CLR);
> 
> Use writel() and you don't have to wrap the line.
Please refer to my earlier response regarding using _relaxed() variants.
> 
>> +		if (dm_rx_st)
>> +			writel_relaxed(dm_rx_st, gi2c->base +
>> +				       SE_DMA_RX_IRQ_CLR);
>> +		/* Ensure all writes are done before returning from ISR. */
> 
> That's not what wmb does.
I will drop it.
> 
>> +		wmb();
>> +	}
>> +	/* if this is err with done-bit not set, handle that thr' timeout. */
> 
> Is "thr'" should for "through"?
Yes. I will make it clear.
> 
>> +	if (m_stat & M_CMD_DONE_EN)
>> +		complete(&gi2c->xfer);
>> +	else if ((dm_tx_st & TX_DMA_DONE) || (dm_rx_st & RX_DMA_DONE))
>> +		complete(&gi2c->xfer);
>> +
>> +	return IRQ_HANDLED;
>> +}
>> +
>> +static int geni_i2c_xfer(struct i2c_adapter *adap,
>> +			 struct i2c_msg msgs[],
>> +			 int num)
>> +{
>> +	struct geni_i2c_dev *gi2c = i2c_get_adapdata(adap);
>> +	int i, ret = 0, timeout = 0;
> 
> No need to initialize these, first use is an assignment.
Ok.
> 
>> +
>> +	gi2c->err = 0;
>> +	gi2c->cur = &msgs[0];
> 
> You're assigning cur in each iteration of the loop below, why do you
> need to do it here as well?
I will remove.
> 
>> +	reinit_completion(&gi2c->xfer);
>> +	ret = pm_runtime_get_sync(gi2c->dev);
>> +	if (ret < 0) {
>> +		dev_err(gi2c->dev, "error turning SE resources:%d\n", ret);
>> +		pm_runtime_put_noidle(gi2c->dev);
>> +		/* Set device in suspended since resume failed */
>> +		pm_runtime_set_suspended(gi2c->dev);
>> +		return ret;
> 
> With the questionable assignment above you're leaving the function with
> gi2c->cur pointing to an object that will soon be invalid.
I will fix the assignment.
> 
>> +	}
>> +
>> +	qcom_geni_i2c_conf(gi2c, 0);
>> +	dev_dbg(gi2c->dev, "i2c xfer:num:%d, msgs:len:%d,flg:%d\n",
>> +				num, msgs[0].len, msgs[0].flags);
> 
> Use dynamic function tracing instead of debug prints for this.
Ok. I will remove the debug statements for now and use dynamic function 
tracing in a different patch.
> 
>> +	for (i = 0; i < num; i++) {
> 
> I suggest that you split out two functions here, one for rx-one-message
> and one for tx-one-message. There are some duplication between the two,
> but not too bad.
Ok.
> 
>> +		int stretch = (i < (num - 1));
>> +		u32 m_param = 0;
>> +		u32 m_cmd = 0;
>> +		dma_addr_t tx_dma = 0;
>> +		dma_addr_t rx_dma = 0;
>> +		enum geni_se_xfer_mode mode = FIFO_MODE;
> 
> No need to initialize mode, as the first use is an assignment.
Ok.
> 
>> +
>> +		m_param |= (stretch ? STOP_STRETCH : 0);
>> +		m_param |= ((msgs[i].addr & 0x7F) << SLV_ADDR_SHFT);
> 
> Drop some parenthesis.
Ok.
> 
>> +
>> +		gi2c->cur = &msgs[i];
>> +		mode = msgs[i].len > 32 ? SE_DMA : FIFO_MODE;
>> +		ret = geni_se_select_mode(gi2c->base, mode);
>> +		if (ret) {
>> +			dev_err(gi2c->dev, "%s: Error mode init %d:%d:%d\n",
>> +				__func__, mode, i, msgs[i].len);
>> +			break;
>> +		}
>> +		if (msgs[i].flags & I2C_M_RD) {
>> +			dev_dbg(gi2c->dev,
>> +				"READ,n:%d,i:%d len:%d, stretch:%d\n",
>> +					num, i, msgs[i].len, stretch);
>> +			geni_write_reg(msgs[i].len,
>> +				       gi2c->base, SE_I2C_RX_TRANS_LEN);
>> +			m_cmd = I2C_READ;
>> +			geni_se_setup_m_cmd(gi2c->base, m_cmd, m_param);
> 
> Drop m_cmd and just write I2C_READ in the function call.
Ok.
> 
>> +			if (mode == SE_DMA) {
>> +				ret = geni_se_rx_dma_prep(gi2c->wrapper_dev,
>> +							gi2c->base, msgs[i].buf,
>> +							msgs[i].len, &rx_dma);
>> +				if (ret) {
>> +					mode = FIFO_MODE;
>> +					ret = geni_se_select_mode(gi2c->base,
>> +								  mode);
>> +				}
>> +			}
>> +		} else {
>> +			dev_dbg(gi2c->dev,
>> +				"WRITE:n:%d,i:%d len:%d, stretch:%d, m_param:0x%x\n",
>> +					num, i, msgs[i].len, stretch, m_param);
>> +			geni_write_reg(msgs[i].len, gi2c->base,
>> +						SE_I2C_TX_TRANS_LEN);
>> +			m_cmd = I2C_WRITE;
>> +			geni_se_setup_m_cmd(gi2c->base, m_cmd, m_param);
>> +			if (mode == SE_DMA) {
>> +				ret = geni_se_tx_dma_prep(gi2c->wrapper_dev,
>> +							gi2c->base, msgs[i].buf,
>> +							msgs[i].len, &tx_dma);
>> +				if (ret) {
>> +					mode = FIFO_MODE;
>> +					ret = geni_se_select_mode(gi2c->base,
>> +								  mode);
>> +				}
>> +			}
>> +			if (mode == FIFO_MODE) /* Get FIFO IRQ */
>> +				geni_write_reg(1, gi2c->base,
>> +						SE_GENI_TX_WATERMARK_REG);
>> +		}
>> +		/* Ensure FIFO write go through before waiting for Done evet */
> 
> That's not what mb does, drop it.
Ok.
> 
>> +		mb();
>> +		timeout = wait_for_completion_timeout(&gi2c->xfer, HZ);
> 
> As written you should just use "ret", but the return value of
> wait_for_completion_timeout() is unsigned long, so change the type of
> timeout instead.
Ok.
> 
>> +		if (!timeout) {
>> +			geni_i2c_err(gi2c, GENI_TIMEOUT);
>> +			gi2c->cur = NULL;
>> +			geni_se_abort_m_cmd(gi2c->base);
>> +			timeout = wait_for_completion_timeout(&gi2c->xfer, HZ);
> 
> What if it takes a fraction above HZ time to complete the previous
> operation, then you might end up here with the completion completed, but
> from the wrong operation.
I will update it to checking the specific "abort" flag using 
readl_poll_timeout.
> 
>> +		}
>> +		gi2c->cur_wr = 0;
>> +		gi2c->cur_rd = 0;
>> +		if (mode == SE_DMA) {
>> +			if (gi2c->err) {
>> +				if (msgs[i].flags != I2C_M_RD)
>> +					writel_relaxed(1, gi2c->base +
>> +							SE_DMA_TX_FSM_RST);
>> +				else
>> +					writel_relaxed(1, gi2c->base +
>> +							SE_DMA_RX_FSM_RST);
>> +				wait_for_completion_timeout(&gi2c->xfer, HZ);
>> +			}
>> +			geni_se_rx_dma_unprep(gi2c->wrapper_dev, rx_dma,
>> +					      msgs[i].len);
> 
> Reduce the length of gi2c->wrapper_dev here by using a local variable,
> so that you get below (or close to) 80 chars.
> 
> Also, in either rx or tx cases above I see only that you prep one of
> thse, and then you're unprepping both.
I will re-organize the tx and rx into 2 separate functions. That way all 
the comments will be taken care of.
> 
>> +			geni_se_tx_dma_unprep(gi2c->wrapper_dev, tx_dma,
>> +					      msgs[i].len);
>> +		}
>> +		ret = gi2c->err;
>> +		if (gi2c->err) {
>> +			dev_err(gi2c->dev, "i2c error :%d\n", gi2c->err);
>> +			break;
>> +		}
>> +	}
>> +	if (ret == 0)
>> +		ret = num;
>> +
>> +	pm_runtime_mark_last_busy(gi2c->dev);
>> +	pm_runtime_put_autosuspend(gi2c->dev);
>> +	gi2c->cur = NULL;
>> +	gi2c->err = 0;
>> +	dev_dbg(gi2c->dev, "i2c txn ret:%d\n", ret);
>> +	return ret;
>> +}
> [..]
>> +static int geni_i2c_probe(struct platform_device *pdev)
>> +{
>> +	struct geni_i2c_dev *gi2c;
>> +	struct resource *res;
>> +	int ret;
>> +
>> +	gi2c = devm_kzalloc(&pdev->dev, sizeof(*gi2c), GFP_KERNEL);
>> +	if (!gi2c)
>> +		return -ENOMEM;
>> +
>> +	gi2c->dev = &pdev->dev;
>> +
>> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +	if (!res)
>> +		return -EINVAL;
> 
> Put this right before devm_ioremap_resource() and drop the error check.
Ok.
> 
>> +
>> +	gi2c->wrapper_dev = pdev->dev.parent;
>> +	gi2c->i2c_rsc.wrapper_dev = pdev->dev.parent;
> 
> As suggested in the other patch, if you have an helper function in the
> wrapper driver that initializes the i2c_rsc then this could fill out the
> actual wrapper, instead of just the device.
Ok.
> 
>> +	gi2c->i2c_rsc.se_clk = devm_clk_get(&pdev->dev, "se-clk");
>> +	if (IS_ERR(gi2c->i2c_rsc.se_clk)) {
>> +		ret = PTR_ERR(gi2c->i2c_rsc.se_clk);
>> +		dev_err(&pdev->dev, "Err getting SE Core clk %d\n", ret);
>> +		return ret;
>> +	}
>> +
>> +	gi2c->base = devm_ioremap_resource(gi2c->dev, res);
>> +	if (IS_ERR(gi2c->base))
>> +		return PTR_ERR(gi2c->base);
>> +
>> +	gi2c->i2c_rsc.geni_pinctrl = devm_pinctrl_get(&pdev->dev);
> 
> Drop all the pinctrl stuff. You might still want to call
> pinctrl_pm_select_{idle,default,sleep}_state(), in the various stages of
> PM state though.
Ok.
> 
>> +	if (IS_ERR_OR_NULL(gi2c->i2c_rsc.geni_pinctrl)) {
>> +		dev_err(&pdev->dev, "No pinctrl config specified\n");
>> +		ret = PTR_ERR(gi2c->i2c_rsc.geni_pinctrl);
>> +		return ret;
>> +	}
> [..]
>> +static int geni_i2c_runtime_resume(struct device *dev)
>> +{
>> +	int ret;
>> +	struct geni_i2c_dev *gi2c = dev_get_drvdata(dev);
>> +
>> +	ret = geni_se_resources_on(&gi2c->i2c_rsc);
>> +	if (ret)
>> +		return ret;
>> +
>> +	if (!unlikely(gi2c->tx_wm)) {
> 
> Drop the unlikely()
Ok.
> 
>> +		int proto = geni_se_get_proto(gi2c->base);
>> +		int gi2c_tx_depth = geni_se_get_tx_fifo_depth(gi2c->base);
>> +
>> +		if (unlikely(proto != I2C)) {
> 
> Has this changes since probe? Can't we verify that the proto is correct
> during probe and then just trust that the hardware doesn't change
> function?
Yes we can do that. I will move the check to the probe.
> 
>> +			dev_err(gi2c->dev, "Invalid proto %d\n", proto);
>> +			geni_se_resources_off(&gi2c->i2c_rsc);
>> +			return -ENXIO;
>> +		}
>> +
>> +		gi2c->tx_wm = gi2c_tx_depth - 1;
>> +		geni_se_init(gi2c->base, gi2c->tx_wm, gi2c_tx_depth);
>> +		geni_se_config_packing(gi2c->base, 8, 4, true);
>> +		dev_dbg(gi2c->dev, "i2c fifo/se-dma mode. fifo depth:%d\n",
>> +			gi2c_tx_depth);
>> +	}
>> +	enable_irq(gi2c->irq);
>> +
>> +	return 0;
>> +}
> [..]
>> +MODULE_LICENSE("GPL v2");
>> +MODULE_ALIAS("platform:i2c_geni");
> 
> What will reference the kernel module by this alias?
I will remove it.
> 
>> -- 
>> Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
>> a Linux Foundation Collaborative Project
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html

-- 
Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

  reply	other threads:[~2018-02-27 13:16 UTC|newest]

Thread overview: 42+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-01-13  1:05 [PATCH v2 0/7] Introduce GENI SE Controller Driver Karthikeyan Ramasubramanian
2018-01-13  1:05 ` [PATCH v2 1/7] qcom-geni-se: Add QCOM GENI SE Driver summary Karthikeyan Ramasubramanian
2018-01-16 16:55   ` Bjorn Andersson
2018-01-29 21:52     ` Karthik Ramasubramanian
2018-01-13  1:05 ` [PATCH v2 2/7] dt-bindings: soc: qcom: Add device tree binding for GENI SE Karthikeyan Ramasubramanian
2018-01-17  6:25   ` Bjorn Andersson
2018-01-19 22:53     ` Rob Herring
2018-02-26 21:24     ` Karthik Ramasubramanian
2018-01-13  1:05 ` [PATCH v2 4/7] dt-bindings: i2c: Add device tree bindings for GENI I2C Controller Karthikeyan Ramasubramanian
     [not found]   ` <1515805547-22816-5-git-send-email-kramasub-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
2018-01-17  6:31     ` Bjorn Andersson
2018-02-26 21:28       ` Karthik Ramasubramanian
2018-01-13  1:05 ` [PATCH v2 6/7] dt-bindings: serial: Add bindings for GENI based UART Controller Karthikeyan Ramasubramanian
2018-01-17  6:35   ` Bjorn Andersson
2018-02-27 13:25     ` Karthik Ramasubramanian
2018-01-13  1:05 ` [PATCH v2 7/7] tty: serial: msm_geni_serial: Add serial driver support for GENI based QUP Karthikeyan Ramasubramanian
2018-01-18 19:43   ` Bjorn Andersson
2018-01-19  7:12   ` Bjorn Andersson
2018-02-27 15:07     ` Karthik Ramasubramanian
2018-01-24 23:07   ` [v2, " Evan Green
2018-02-14 11:04   ` [PATCH v2 " Amit Kucheria
2018-02-23 18:06   ` [v2, " Guenter Roeck
2018-02-27 13:23     ` Karthik Ramasubramanian
2018-02-23 19:05   ` Doug Anderson
     [not found] ` <1515805547-22816-1-git-send-email-kramasub-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
2018-01-13  1:05   ` [PATCH v2 3/7] soc: qcom: Add GENI based QUP Wrapper driver Karthikeyan Ramasubramanian
2018-01-17  6:20     ` Bjorn Andersson
2018-01-18  9:13       ` Rajendra Nayak
2018-01-18 16:57         ` Bjorn Andersson
2018-01-19 22:57           ` Rob Herring
2018-01-31 19:02             ` Karthik Ramasubramanian
     [not found]               ` <1abb0679-1997-9b70-30bd-d3472cea7053-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
2018-02-05 23:53                 ` Bjorn Andersson
2018-01-31 18:58       ` Karthik Ramasubramanian
2018-02-05 23:50         ` Bjorn Andersson
     [not found]     ` <1515805547-22816-4-git-send-email-kramasub-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
2018-01-24 23:06       ` [v2,3/7] " Evan Green
2018-01-26 19:38     ` Doug Anderson
2018-02-14 11:07     ` [PATCH v2 3/7] " Amit Kucheria
2018-02-16 20:44       ` Karthik Ramasubramanian
2018-01-13  1:05   ` [PATCH v2 5/7] i2c: i2c-qcom-geni: Add bus driver for the Qualcomm GENI I2C controller Karthikeyan Ramasubramanian
     [not found]     ` <1515805547-22816-6-git-send-email-kramasub-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
2018-01-18  5:23       ` Bjorn Andersson
2018-02-27 13:16         ` Karthik Ramasubramanian [this message]
2018-01-24 23:07     ` [v2, " Evan Green
2018-01-19 18:32   ` [PATCH v2 0/7] Introduce GENI SE Controller Driver Randy Dunlap
2018-01-31 18:59     ` Karthik Ramasubramanian

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=4ff97d07-c28b-0bbb-5891-e696bce6a3c9@codeaurora.org \
    --to=kramasub@codeaurora.org \
    --cc=andy.gross@linaro.org \
    --cc=bjorn.andersson@linaro.org \
    --cc=corbet@lwn.net \
    --cc=david.brown@linaro.org \
    --cc=devicetree@vger.kernel.org \
    --cc=girishm@codeaurora.org \
    --cc=gregkh@linuxfoundation.org \
    --cc=jslaby@suse.com \
    --cc=linux-arm-msm@vger.kernel.org \
    --cc=linux-doc@vger.kernel.org \
    --cc=linux-i2c@vger.kernel.org \
    --cc=linux-serial@vger.kernel.org \
    --cc=mark.rutland@arm.com \
    --cc=robh+dt@kernel.org \
    --cc=sdharia@codeaurora.org \
    --cc=wsa@the-dreams.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).