linux-i2c.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 2.6.38 1/1] scx200_acb.c: Add plain i2c (master_xfer / I2C_FUNC_I2C)
@ 2011-08-08 19:29 Tomas
       [not found] ` <CAN=yz1iG02kqUm0pJ8sXgJ81JJerJTmWuGAkPo6UUGBTE_6Zpw-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
  0 siblings, 1 reply; 2+ messages in thread
From: Tomas @ 2011-08-08 19:29 UTC (permalink / raw)
  To: linux-i2c-u79uwXL29TY76Z2rM5mHXA; +Cc: jim.cromie-Re5JQEeQqe8AvxtiuMwx3w

From: Tomas Menzl <tomasamot-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>

Add master_xfer / I2C_FUNC_I2C by simply reusing existing FSM
scx200_acb_machine.
This adds possibility to do direct read/write on an i2c device or use
I2C_RDWR ioctl in addition to existing SM Bus API.

Signed-off-by: Tomas Menzl <tomasamot-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
----
Added plain I2C interface so that one can use plain read/write (among
others). Needed plain I2C multibyte read which is not possible with SM
bus.
Tested on Voyage Linux 0.75 (http://linux.voyage.hk/, based on Debian
Squeeze, 2.6.38, this module is original/vanilla - i.e. patch
applicable to any current version) on PC Engine WRAP 2C with Microchip
ADC MCP3421.
SM BUS interface intact, read/write worked for me (only tested single
message transactions, do not have HW to test combined transaction but
they are there...).

--- linux-source-2.6.38-voyage/drivers/i2c/busses/scx200_acb.c	2011-08-05
19:44:11.000000000 +0200
+++ linux-source-2.6.38-voyage.new/drivers/i2c/busses/scx200_acb.c	2011-08-05
22:06:18.000000000 +0200
@@ -86,6 +86,7 @@ struct scx200_acb_iface {
 	u8 *ptr;
 	char needs_reset;
 	unsigned len;
+	char skip_stop;
 };

 /* Register Definitions */
@@ -130,6 +131,7 @@ static void scx200_acb_machine(struct sc
 			scx200_acb_state_name[iface->state]);

 		iface->state = state_idle;
+		iface->skip_stop = 0;
 		iface->result = -ENXIO;

 		outb(inb(ACBCTL1) | ACBCTL1_STOP, ACBCTL1);
@@ -191,7 +193,8 @@ static void scx200_acb_machine(struct sc
 		if (iface->len == 1) {
 			iface->result = 0;
 			iface->state = state_idle;
-			outb(inb(ACBCTL1) | ACBCTL1_STOP, ACBCTL1);
+			if (!iface->skip_stop)
+				outb(inb(ACBCTL1) | ACBCTL1_STOP, ACBCTL1);
 		}

 		*iface->ptr++ = inb(ACBSDA);
@@ -203,7 +206,8 @@ static void scx200_acb_machine(struct sc
 		if (iface->len == 0) {
 			iface->result = 0;
 			iface->state = state_idle;
-			outb(inb(ACBCTL1) | ACBCTL1_STOP, ACBCTL1);
+			if (!iface->skip_stop)
+				outb(inb(ACBCTL1) | ACBCTL1_STOP, ACBCTL1);
 			break;
 		}

@@ -222,6 +226,7 @@ static void scx200_acb_machine(struct sc
 		iface->len, status);

 	iface->state = state_idle;
+	iface->skip_stop = 0;
 	iface->result = -EIO;
 	iface->needs_reset = 1;
 }
@@ -277,6 +282,104 @@ static void scx200_acb_reset(struct scx2
 	outb(inb(ACBCST) | ACBCST_BB, ACBCST);
 }

+/*
+ * Generic i2c master transfer entrypoint.
+ *
+ * Basically copy of part of scx200_acb_smbus_xfer where we use existing
+ * scx200_acb_machine which already supports simple i2c with any data length
+ * (not only 0 and 1 as used by smbus) by using:
+ *
+ *         state_quick -> ( state_read | state_write )+ -> state_idle
+ *
+ * Added flag skip_stop to support multimessage ops in scx200_acb_machine
+ * to be able skip stop bit between messages in state_read/state_write:
+ *
+ *       S Addr Rd [A] [Data] NA S Addr Wr [A] Data [A] P
+ *                              ^--- no stop bit!       ^--- stop bit
+ *
+ * Still missing 10b address, pec, etc...
+ */
+static int scx200_acb_i2c_master_xfer(struct i2c_adapter *adapter,
+				       struct i2c_msg *msgs,
+				       int num)
+{
+	struct scx200_acb_iface *iface = i2c_get_adapdata(adapter);
+	int   rc = 0;
+	char  rw;
+	int   len;
+	u8   *buffer;
+	u16   address;
+
+	mutex_lock(&iface->mutex);
+	while (num > 0) {
+		if (msgs->flags & I2C_M_TEN) {
+			dev_err(&adapter->dev,
+			"10b i2c address supported\n");
+			rc = -EINVAL;
+			break;
+		}
+
+		rw = (msgs->flags & I2C_M_RD) != 0;
+		address = (msgs->addr << 1) | rw;
+		len = msgs->len;
+		buffer = msgs->buf;
+
+		dev_dbg(&adapter->dev,
+			"address=0x%x, len=%d, read=%d\n", msgs->addr, len, rw);
+
+		if (!len && rw == I2C_SMBUS_READ) {
+			dev_dbg(&adapter->dev, "zero length read\n");
+			rc = -EINVAL;
+			break;
+		}
+
+		iface->address_byte = address;
+		iface->command = 0;
+		iface->ptr = buffer;
+		iface->len = len;
+		iface->result = -EINVAL;
+		iface->needs_reset = 0;
+		if (num > 1)
+			iface->skip_stop = 1;
+		else
+			iface->skip_stop = 0;
+
+		/* send start */
+		outb(inb(ACBCTL1) | ACBCTL1_START, ACBCTL1);
+
+		/* no command */
+		iface->state = state_quick;
+
+		while (iface->state != state_idle)
+			scx200_acb_poll(iface);
+
+		if (iface->needs_reset)
+			scx200_acb_reset(iface);
+
+		if (iface->result)
+			break;
+
+#ifdef DEBUG
+		dev_dbg(&adapter->dev, "transfer done, result: %d", rc);
+		if (buffer) {
+			int i;
+			printk(KERN_DEBUG " data:");
+			for (i = 0; i < len; ++i)
+				printk(KERN_DEBUG " %02x", buffer[i]);
+		}
+		printk(KERN_DEBUG "\n");
+#endif
+		++rc;
+		--num;
+		++msgs;
+	}
+	mutex_unlock(&iface->mutex);
+
+	iface->skip_stop = 0;
+	return rc;
+}
+
+
 static s32 scx200_acb_smbus_xfer(struct i2c_adapter *adapter,
 				 u16 address, unsigned short flags,
 				 char rw, u8 command, int size,
@@ -338,6 +441,7 @@ static s32 scx200_acb_smbus_xfer(struct
 	iface->len = len;
 	iface->result = -EINVAL;
 	iface->needs_reset = 0;
+	iface->skip_stop = 0;

 	outb(inb(ACBCTL1) | ACBCTL1_START, ACBCTL1);

@@ -377,12 +481,13 @@ static u32 scx200_acb_func(struct i2c_ad
 {
 	return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
 	       I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
-	       I2C_FUNC_SMBUS_I2C_BLOCK;
+	       I2C_FUNC_SMBUS_I2C_BLOCK | I2C_FUNC_I2C;
 }

 /* For now, we only handle combined mode (smbus) */
 static const struct i2c_algorithm scx200_acb_algorithm = {
 	.smbus_xfer	= scx200_acb_smbus_xfer,
+	.master_xfer    = scx200_acb_i2c_master_xfer,
 	.functionality	= scx200_acb_func,
 };

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2011-08-09 20:51 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-08-08 19:29 [PATCH 2.6.38 1/1] scx200_acb.c: Add plain i2c (master_xfer / I2C_FUNC_I2C) Tomas
     [not found] ` <CAN=yz1iG02kqUm0pJ8sXgJ81JJerJTmWuGAkPo6UUGBTE_6Zpw-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2011-08-09 20:51   ` Jim Cromie

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).