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