efb53c
From 80d7fcc321f01c7a69de38a20de726fe8e1149ee Mon Sep 17 00:00:00 2001
efb53c
From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= <noralf@tronnes.org>
efb53c
Date: Tue, 1 Nov 2016 15:15:41 +0100
efb53c
Subject: [PATCH] i2c: bcm2835: Add debug support
efb53c
MIME-Version: 1.0
efb53c
Content-Type: text/plain; charset=UTF-8
efb53c
Content-Transfer-Encoding: 8bit
efb53c
efb53c
This adds a debug module parameter to aid in debugging transfer issues
efb53c
by printing info to the kernel log. When enabled, status values are
efb53c
collected in the interrupt routine and msg info in
efb53c
bcm2835_i2c_start_transfer(). This is done in a way that tries to avoid
efb53c
affecting timing. Having printk in the isr can mask issues.
efb53c
efb53c
debug values (additive):
efb53c
1: Print info on error
efb53c
2: Print info on all transfers
efb53c
3: Print messages before transfer is started
efb53c
efb53c
The value can be changed at runtime:
efb53c
/sys/module/i2c_bcm2835/parameters/debug
efb53c
efb53c
Example output, debug=3:
efb53c
[  747.114448] bcm2835_i2c_xfer: msg(1/2) write addr=0x54, len=2 flags= [i2c1]
efb53c
[  747.114463] bcm2835_i2c_xfer: msg(2/2) read addr=0x54, len=32 flags= [i2c1]
efb53c
[  747.117809] start_transfer: msg(1/2) write addr=0x54, len=2 flags= [i2c1]
efb53c
[  747.117825] isr: remain=2, status=0x30000055 : TA TXW TXD TXE  [i2c1]
efb53c
[  747.117839] start_transfer: msg(2/2) read addr=0x54, len=32 flags= [i2c1]
efb53c
[  747.117849] isr: remain=32, status=0xd0000039 : TA RXR TXD RXD  [i2c1]
efb53c
[  747.117861] isr: remain=20, status=0xd0000039 : TA RXR TXD RXD  [i2c1]
efb53c
[  747.117870] isr: remain=8, status=0x32 : DONE TXD RXD  [i2c1]
efb53c
efb53c
Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
efb53c
---
efb53c
 drivers/i2c/busses/i2c-bcm2835.c | 99 +++++++++++++++++++++++++++++++-
efb53c
 1 file changed, 98 insertions(+), 1 deletion(-)
efb53c
efb53c
diff --git a/drivers/i2c/busses/i2c-bcm2835.c b/drivers/i2c/busses/i2c-bcm2835.c
efb53c
index 44deae78913e5..b7110eb2163e7 100644
efb53c
--- a/drivers/i2c/busses/i2c-bcm2835.c
efb53c
+++ b/drivers/i2c/busses/i2c-bcm2835.c
efb53c
@@ -56,6 +56,18 @@
efb53c
 #define BCM2835_I2C_CDIV_MIN	0x0002
efb53c
 #define BCM2835_I2C_CDIV_MAX	0xFFFE
efb53c
 
efb53c
+static unsigned int debug;
efb53c
+module_param(debug, uint, 0644);
efb53c
+MODULE_PARM_DESC(debug, "1=err, 2=isr, 3=xfer");
efb53c
+
efb53c
+#define BCM2835_DEBUG_MAX	512
efb53c
+struct bcm2835_debug {
efb53c
+	struct i2c_msg *msg;
efb53c
+	int msg_idx;
efb53c
+	size_t remain;
efb53c
+	u32 status;
efb53c
+};
efb53c
+
efb53c
 struct bcm2835_i2c_dev {
efb53c
 	struct device *dev;
efb53c
 	void __iomem *regs;
efb53c
@@ -69,8 +81,78 @@ struct bcm2835_i2c_dev {
efb53c
 	u32 msg_err;
efb53c
 	u8 *msg_buf;
efb53c
 	size_t msg_buf_remaining;
efb53c
+	struct bcm2835_debug debug[BCM2835_DEBUG_MAX];
efb53c
+	unsigned int debug_num;
efb53c
+	unsigned int debug_num_msgs;
efb53c
 };
efb53c
 
efb53c
+static inline void bcm2835_debug_add(struct bcm2835_i2c_dev *i2c_dev, u32 s)
efb53c
+{
efb53c
+	if (!i2c_dev->debug_num_msgs || i2c_dev->debug_num >= BCM2835_DEBUG_MAX)
efb53c
+		return;
efb53c
+
efb53c
+	i2c_dev->debug[i2c_dev->debug_num].msg = i2c_dev->curr_msg;
efb53c
+	i2c_dev->debug[i2c_dev->debug_num].msg_idx =
efb53c
+				i2c_dev->debug_num_msgs - i2c_dev->num_msgs;
efb53c
+	i2c_dev->debug[i2c_dev->debug_num].remain = i2c_dev->msg_buf_remaining;
efb53c
+	i2c_dev->debug[i2c_dev->debug_num].status = s;
efb53c
+	i2c_dev->debug_num++;
efb53c
+}
efb53c
+
efb53c
+static void bcm2835_debug_print_status(struct bcm2835_i2c_dev *i2c_dev,
efb53c
+				       struct bcm2835_debug *d)
efb53c
+{
efb53c
+	u32 s = d->status;
efb53c
+
efb53c
+	pr_info("isr: remain=%zu, status=0x%x : %s%s%s%s%s%s%s%s%s%s [i2c%d]\n",
efb53c
+		d->remain, s,
efb53c
+		s & BCM2835_I2C_S_TA ? "TA " : "",
efb53c
+		s & BCM2835_I2C_S_DONE ? "DONE " : "",
efb53c
+		s & BCM2835_I2C_S_TXW ? "TXW " : "",
efb53c
+		s & BCM2835_I2C_S_RXR ? "RXR " : "",
efb53c
+		s & BCM2835_I2C_S_TXD ? "TXD " : "",
efb53c
+		s & BCM2835_I2C_S_RXD ? "RXD " : "",
efb53c
+		s & BCM2835_I2C_S_TXE ? "TXE " : "",
efb53c
+		s & BCM2835_I2C_S_RXF ? "RXF " : "",
efb53c
+		s & BCM2835_I2C_S_ERR ? "ERR " : "",
efb53c
+		s & BCM2835_I2C_S_CLKT ? "CLKT " : "",
efb53c
+		i2c_dev->adapter.nr);
efb53c
+}
efb53c
+
efb53c
+static void bcm2835_debug_print_msg(struct bcm2835_i2c_dev *i2c_dev,
efb53c
+				    struct i2c_msg *msg, int i, int total,
efb53c
+				    const char *fname)
efb53c
+{
efb53c
+	pr_info("%s: msg(%d/%d) %s addr=0x%02x, len=%u flags=%s%s%s%s%s%s%s [i2c%d]\n",
efb53c
+		fname, i, total,
efb53c
+		msg->flags & I2C_M_RD ? "read" : "write", msg->addr, msg->len,
efb53c
+		msg->flags & I2C_M_TEN ? "TEN" : "",
efb53c
+		msg->flags & I2C_M_RECV_LEN ? "RECV_LEN" : "",
efb53c
+		msg->flags & I2C_M_NO_RD_ACK ? "NO_RD_ACK" : "",
efb53c
+		msg->flags & I2C_M_IGNORE_NAK ? "IGNORE_NAK" : "",
efb53c
+		msg->flags & I2C_M_REV_DIR_ADDR ? "REV_DIR_ADDR" : "",
efb53c
+		msg->flags & I2C_M_NOSTART ? "NOSTART" : "",
efb53c
+		msg->flags & I2C_M_STOP ? "STOP" : "",
efb53c
+		i2c_dev->adapter.nr);
efb53c
+}
efb53c
+
efb53c
+static void bcm2835_debug_print(struct bcm2835_i2c_dev *i2c_dev)
efb53c
+{
efb53c
+	struct bcm2835_debug *d;
efb53c
+	unsigned int i;
efb53c
+
efb53c
+	for (i = 0; i < i2c_dev->debug_num; i++) {
efb53c
+		d = &i2c_dev->debug[i];
efb53c
+		if (d->status == ~0)
efb53c
+			bcm2835_debug_print_msg(i2c_dev, d->msg, d->msg_idx,
efb53c
+				i2c_dev->debug_num_msgs, "start_transfer");
efb53c
+		else
efb53c
+			bcm2835_debug_print_status(i2c_dev, d);
efb53c
+	}
efb53c
+	if (i2c_dev->debug_num >= BCM2835_DEBUG_MAX)
efb53c
+		pr_info("BCM2835_DEBUG_MAX reached\n");
efb53c
+}
efb53c
+
efb53c
 static inline void bcm2835_i2c_writel(struct bcm2835_i2c_dev *i2c_dev,
efb53c
 				      u32 reg, u32 val)
efb53c
 {
efb53c
@@ -189,6 +271,7 @@ static void bcm2835_i2c_start_transfer(struct bcm2835_i2c_dev *i2c_dev)
efb53c
 	bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_A, msg->addr);
efb53c
 	bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_DLEN, msg->len);
efb53c
 	bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_C, c);
efb53c
+	bcm2835_debug_add(i2c_dev, ~0);
efb53c
 }
efb53c
 
efb53c
 /*
efb53c
@@ -206,6 +289,7 @@ static irqreturn_t bcm2835_i2c_isr(int this_irq, void *data)
efb53c
 	u32 val, err;
efb53c
 
efb53c
 	val = bcm2835_i2c_readl(i2c_dev, BCM2835_I2C_S);
efb53c
+	bcm2835_debug_add(i2c_dev, val);
efb53c
 
efb53c
 	err = val & (BCM2835_I2C_S_CLKT | BCM2835_I2C_S_ERR);
efb53c
 	if (err) {
efb53c
@@ -272,6 +356,13 @@ static int bcm2835_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
efb53c
 	unsigned long time_left;
efb53c
 	int i, ret;
efb53c
 
efb53c
+	if (debug)
efb53c
+		i2c_dev->debug_num_msgs = num;
efb53c
+
efb53c
+	if (debug > 2)
efb53c
+		for (i = 0; i < num; i++)
efb53c
+			bcm2835_debug_print_msg(i2c_dev, &msgs[i], i + 1, num, __func__);
efb53c
+
efb53c
 	for (i = 0; i < (num - 1); i++)
efb53c
 		if (msgs[i].flags & I2C_M_RD) {
efb53c
 			dev_warn_once(i2c_dev->dev,
efb53c
@@ -291,6 +382,10 @@ static int bcm2835_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
efb53c
 
efb53c
 	time_left = wait_for_completion_timeout(&i2c_dev->completion,
efb53c
 						adap->timeout);
efb53c
+	if (debug > 1 || (debug && (!time_left || i2c_dev->msg_err)))
efb53c
+		bcm2835_debug_print(i2c_dev);
efb53c
+	i2c_dev->debug_num_msgs = 0;
efb53c
+	i2c_dev->debug_num = 0;
efb53c
 	if (!time_left) {
efb53c
 		bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_C,
efb53c
 				   BCM2835_I2C_C_CLEAR);
efb53c
@@ -301,7 +396,9 @@ static int bcm2835_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
efb53c
 	if (!i2c_dev->msg_err)
efb53c
 		return num;
efb53c
 
efb53c
-	dev_dbg(i2c_dev->dev, "i2c transfer failed: %x\n", i2c_dev->msg_err);
efb53c
+	if (debug)
efb53c
+		dev_err(i2c_dev->dev, "i2c transfer failed: %x\n",
efb53c
+			i2c_dev->msg_err);
efb53c
 
efb53c
 	if (i2c_dev->msg_err & BCM2835_I2C_S_ERR)
efb53c
 		return -EREMOTEIO;