linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH][I2C] Marvell mv64xxx i2c driver
@ 2005-02-08 23:27 Mark A. Greer
  2005-02-09  0:01 ` Bartlomiej Zolnierkiewicz
  0 siblings, 1 reply; 23+ messages in thread
From: Mark A. Greer @ 2005-02-08 23:27 UTC (permalink / raw)
  To: Greg KH; +Cc: LM Sensors, lkml

[-- Attachment #1: Type: text/plain, Size: 371 bytes --]

Marvell makes a line of host bridge for PPC and MIPS systems.  On those 
bridges is an i2c controller.  This patch adds the driver for that i2c 
controller.

Please apply.

Depends on patch submitted by Jean Delvare: 
http://archives.andrew.net.au/lm-sensors/msg29405.html

Signed-off-by: Mark A. Greer <mgreer@mvista.com>

[Note: This patch is a *replacement* patch]
--

[-- Attachment #2: i2c_7.patch --]
[-- Type: text/plain, Size: 20549 bytes --]

diff -Nru a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
--- a/drivers/i2c/busses/Kconfig	2005-02-08 16:24:33 -07:00
+++ b/drivers/i2c/busses/Kconfig	2005-02-08 16:24:33 -07:00
@@ -486,4 +486,14 @@
 	  This driver can also be built as a module.  If so, the module
 	  will be called i2c-pca-isa.
 
+config I2C_MV64XXX
+	tristate "Marvell mv64xxx I2C Controller"
+	depends on I2C && MV64X60 && EXPERIMENTAL
+	help
+	  If you say yes to this option, support will be included for the
+	  built-in I2C interface on the Marvell 64xxx line of host bridges.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called i2c-mv64xxx.
+
 endmenu
diff -Nru a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
--- a/drivers/i2c/busses/Makefile	2005-02-08 16:24:33 -07:00
+++ b/drivers/i2c/busses/Makefile	2005-02-08 16:24:33 -07:00
@@ -21,6 +21,7 @@
 obj-$(CONFIG_I2C_IXP4XX)	+= i2c-ixp4xx.o
 obj-$(CONFIG_I2C_KEYWEST)	+= i2c-keywest.o
 obj-$(CONFIG_I2C_MPC)		+= i2c-mpc.o
+obj-$(CONFIG_I2C_MV64XXX)	+= i2c-mv64xxx.o
 obj-$(CONFIG_I2C_NFORCE2)	+= i2c-nforce2.o
 obj-$(CONFIG_I2C_PARPORT)	+= i2c-parport.o
 obj-$(CONFIG_I2C_PARPORT_LIGHT)	+= i2c-parport-light.o
diff -Nru a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/drivers/i2c/busses/i2c-mv64xxx.c	2005-02-08 16:24:33 -07:00
@@ -0,0 +1,606 @@
+/*
+ * drivers/i2c/busses/i2c-mv64xxx.c
+ * 
+ * Driver for the i2c controller on the Marvell line of host bridges for MIPS
+ * and PPC (e.g, gt642[46]0, mv643[46]0, mv644[46]0).
+ *
+ * Author: Mark A. Greer <mgreer@mvista.com>
+ *
+ * 2005 (c) MontaVista, Software, Inc.  This file is licensed under
+ * the terms of the GNU General Public License version 2.  This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/mv643xx.h>
+#include <asm/io.h>
+
+/* Register defines */
+#define	MV64XXX_I2C_REG_SLAVE_ADDR			0x00
+#define	MV64XXX_I2C_REG_DATA				0x04
+#define	MV64XXX_I2C_REG_CONTROL				0x08
+#define	MV64XXX_I2C_REG_STATUS				0x0c
+#define	MV64XXX_I2C_REG_BAUD				0x0c
+#define	MV64XXX_I2C_REG_EXT_SLAVE_ADDR			0x10
+#define	MV64XXX_I2C_REG_SOFT_RESET			0x1c
+
+#define	MV64XXX_I2C_REG_CONTROL_ACK			0x00000004
+#define	MV64XXX_I2C_REG_CONTROL_IFLG			0x00000008
+#define	MV64XXX_I2C_REG_CONTROL_STOP			0x00000010
+#define	MV64XXX_I2C_REG_CONTROL_START			0x00000020
+#define	MV64XXX_I2C_REG_CONTROL_TWSIEN			0x00000040
+#define	MV64XXX_I2C_REG_CONTROL_INTEN			0x00000080
+
+/* Ctlr status values */
+#define	MV64XXX_I2C_STATUS_BUS_ERR			0x00
+#define	MV64XXX_I2C_STATUS_MAST_START			0x08
+#define	MV64XXX_I2C_STATUS_MAST_REPEAT_START		0x10
+#define	MV64XXX_I2C_STATUS_MAST_WR_ADDR_ACK		0x18
+#define	MV64XXX_I2C_STATUS_MAST_WR_ADDR_NO_ACK		0x20
+#define	MV64XXX_I2C_STATUS_MAST_WR_ACK			0x28
+#define	MV64XXX_I2C_STATUS_MAST_WR_NO_ACK		0x30
+#define	MV64XXX_I2C_STATUS_MAST_LOST_ARB		0x38
+#define	MV64XXX_I2C_STATUS_MAST_RD_ADDR_ACK		0x40
+#define	MV64XXX_I2C_STATUS_MAST_RD_ADDR_NO_ACK		0x48
+#define	MV64XXX_I2C_STATUS_MAST_RD_DATA_ACK		0x50
+#define	MV64XXX_I2C_STATUS_MAST_RD_DATA_NO_ACK		0x58
+#define	MV64XXX_I2C_STATUS_MAST_WR_ADDR_2_ACK		0xd0
+#define	MV64XXX_I2C_STATUS_MAST_WR_ADDR_2_NO_ACK	0xd8
+#define	MV64XXX_I2C_STATUS_MAST_RD_ADDR_2_ACK		0xe0
+#define	MV64XXX_I2C_STATUS_MAST_RD_ADDR_2_NO_ACK	0xe8
+#define	MV64XXX_I2C_STATUS_NO_STATUS			0xf8
+
+/* Driver states */
+enum {
+	MV64XXX_I2C_STATE_INVALID,
+	MV64XXX_I2C_STATE_IDLE,
+	MV64XXX_I2C_STATE_WAITING_FOR_START_COND,
+	MV64XXX_I2C_STATE_WAITING_FOR_ADDR_1_ACK,
+	MV64XXX_I2C_STATE_WAITING_FOR_ADDR_2_ACK,
+	MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_ACK,
+	MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_DATA,
+	MV64XXX_I2C_STATE_ABORTING,
+};
+
+/* Driver actions */
+enum {
+	MV64XXX_I2C_ACTION_INVALID,
+	MV64XXX_I2C_ACTION_CONTINUE,
+	MV64XXX_I2C_ACTION_SEND_START,
+	MV64XXX_I2C_ACTION_SEND_ADDR_1,
+	MV64XXX_I2C_ACTION_SEND_ADDR_2,
+	MV64XXX_I2C_ACTION_SEND_DATA,
+	MV64XXX_I2C_ACTION_RCV_DATA,
+	MV64XXX_I2C_ACTION_RCV_DATA_STOP,
+	MV64XXX_I2C_ACTION_SEND_STOP,
+};
+
+struct mv64xxx_i2c_data {
+	int			irq;
+	u32			state;
+	u32			action;
+	u32			cntl_bits;
+	void __iomem		*reg_base;
+	u32			reg_base_p;
+	u32			addr1;
+	u32			addr2;
+	u32			bytes_left;
+	u32			byte_posn;
+	u32			block;
+	int			rc;
+	u32			freq_m;
+	u32			freq_n;
+	wait_queue_head_t	waitq;
+	spinlock_t		lock;
+	struct i2c_msg		*msg;
+	struct i2c_adapter	adapter;
+};
+
+/*
+ *****************************************************************************
+ *
+ *	Finite State Machine & Interrupt Routines
+ *
+ *****************************************************************************
+ */
+static void
+mv64xxx_i2c_fsm(struct mv64xxx_i2c_data *drv_data, u32 status)
+{
+	/*
+	 * If state is idle, then this is likely the remnants of an old
+	 * operation that driver has given up on or the user has killed.
+	 * If so, issue the stop condition and go to idle.
+	 */
+	if (drv_data->state == MV64XXX_I2C_STATE_IDLE) {
+		drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
+		return;
+	}
+
+	if (drv_data->state == MV64XXX_I2C_STATE_ABORTING) {
+		drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
+		drv_data->state = MV64XXX_I2C_STATE_IDLE;
+		return;
+	}
+
+	/* The status from the ctlr [mostly] tells us what to do next */
+	switch (status) {
+	/* Start condition interrupt */
+	case MV64XXX_I2C_STATUS_MAST_START: /* 0x08 */
+	case MV64XXX_I2C_STATUS_MAST_REPEAT_START: /* 0x10 */
+		drv_data->action = MV64XXX_I2C_ACTION_SEND_ADDR_1;
+		drv_data->state = MV64XXX_I2C_STATE_WAITING_FOR_ADDR_1_ACK;
+		break;
+
+	/* Performing a write */
+	case MV64XXX_I2C_STATUS_MAST_WR_ADDR_ACK: /* 0x18 */
+		if (drv_data->msg->flags & I2C_M_TEN) {
+			drv_data->action = MV64XXX_I2C_ACTION_SEND_ADDR_2;
+			drv_data->state =
+				MV64XXX_I2C_STATE_WAITING_FOR_ADDR_2_ACK;
+			break;
+		}
+		/* FALLTHRU */
+	case MV64XXX_I2C_STATUS_MAST_WR_ADDR_2_ACK: /* 0xd0 */
+	case MV64XXX_I2C_STATUS_MAST_WR_ACK: /* 0x28 */
+		if (drv_data->bytes_left > 0) {
+			drv_data->action = MV64XXX_I2C_ACTION_SEND_DATA;
+			drv_data->state =
+				MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_ACK;
+			drv_data->bytes_left--;
+		} else {
+			drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
+			drv_data->state = MV64XXX_I2C_STATE_IDLE;
+		}
+		break;
+
+	/* Performing a read */
+	case MV64XXX_I2C_STATUS_MAST_RD_ADDR_ACK: /* 40 */
+		if (drv_data->msg->flags & I2C_M_TEN) {
+			drv_data->action = MV64XXX_I2C_ACTION_SEND_ADDR_2;
+			drv_data->state =
+				MV64XXX_I2C_STATE_WAITING_FOR_ADDR_2_ACK;
+			break;
+		}
+		/* FALLTHRU */
+	case MV64XXX_I2C_STATUS_MAST_RD_ADDR_2_ACK: /* 0xe0 */
+		if (drv_data->bytes_left == 0) {
+			drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
+			drv_data->state = MV64XXX_I2C_STATE_IDLE;
+			break;
+		}
+		/* FALLTHRU */
+	case MV64XXX_I2C_STATUS_MAST_RD_DATA_ACK: /* 0x50 */
+		if (status != MV64XXX_I2C_STATUS_MAST_RD_DATA_ACK)
+			drv_data->action = MV64XXX_I2C_ACTION_CONTINUE;
+		else {
+			drv_data->action = MV64XXX_I2C_ACTION_RCV_DATA;
+			drv_data->bytes_left--;
+		}
+		drv_data->state = MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_DATA;
+
+		if (drv_data->bytes_left == 1)
+			drv_data->cntl_bits &= ~MV64XXX_I2C_REG_CONTROL_ACK;
+		break;
+
+	case MV64XXX_I2C_STATUS_MAST_RD_DATA_NO_ACK: /* 0x58 */
+		drv_data->action = MV64XXX_I2C_ACTION_RCV_DATA_STOP;
+		drv_data->state = MV64XXX_I2C_STATE_IDLE;
+		break;
+
+	case MV64XXX_I2C_STATUS_MAST_WR_ADDR_NO_ACK: /* 0x20 */
+	case MV64XXX_I2C_STATUS_MAST_WR_NO_ACK: /* 30 */
+	case MV64XXX_I2C_STATUS_MAST_RD_ADDR_NO_ACK: /* 48 */
+		/* Doesn't seem to be a device at other end */
+		drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
+		drv_data->state = MV64XXX_I2C_STATE_IDLE;
+		drv_data->rc = -ENODEV;
+		break;
+
+	default:
+		dev_err(&drv_data->adapter.dev,
+			"mv64xxx_i2c_fsm: Ctlr Error -- state: 0x%x, "
+			"status: 0x%x, addr: 0x%x, flags: 0x%x\n",
+			 drv_data->state, status, drv_data->msg->addr,
+			 drv_data->msg->flags);
+		drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
+		drv_data->state = MV64XXX_I2C_STATE_IDLE;
+		drv_data->rc = -EIO;
+	}
+
+	return;
+}
+
+static void
+mv64xxx_i2c_do_action(struct mv64xxx_i2c_data *drv_data)
+{
+	switch(drv_data->action) {
+	case MV64XXX_I2C_ACTION_CONTINUE:
+		writel(drv_data->cntl_bits,
+			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+		break;
+
+	case MV64XXX_I2C_ACTION_SEND_START:
+		writel(drv_data->cntl_bits | MV64XXX_I2C_REG_CONTROL_START,
+			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+		break;
+
+	case MV64XXX_I2C_ACTION_SEND_ADDR_1:
+		writel(drv_data->addr1,
+			drv_data->reg_base + MV64XXX_I2C_REG_DATA);
+		writel(drv_data->cntl_bits,
+			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+		break;
+
+	case MV64XXX_I2C_ACTION_SEND_ADDR_2:
+		writel(drv_data->addr2,
+			drv_data->reg_base + MV64XXX_I2C_REG_DATA);
+		writel(drv_data->cntl_bits,
+			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+		break;
+
+	case MV64XXX_I2C_ACTION_SEND_DATA:
+		writel(drv_data->msg->buf[drv_data->byte_posn++],
+			drv_data->reg_base + MV64XXX_I2C_REG_DATA);
+		writel(drv_data->cntl_bits,
+			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+		break;
+
+	case MV64XXX_I2C_ACTION_RCV_DATA:
+		drv_data->msg->buf[drv_data->byte_posn++] =
+			readl(drv_data->reg_base + MV64XXX_I2C_REG_DATA);
+		writel(drv_data->cntl_bits,
+			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+		break;
+
+	case MV64XXX_I2C_ACTION_RCV_DATA_STOP:
+		drv_data->msg->buf[drv_data->byte_posn++] =
+			readl(drv_data->reg_base + MV64XXX_I2C_REG_DATA);
+		drv_data->cntl_bits &= ~MV64XXX_I2C_REG_CONTROL_INTEN;
+		writel(drv_data->cntl_bits | MV64XXX_I2C_REG_CONTROL_STOP,
+			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+		drv_data->block = 0;
+		wake_up_interruptible(&drv_data->waitq);
+		break;
+
+	case MV64XXX_I2C_ACTION_INVALID:
+	default:
+		dev_err(&drv_data->adapter.dev,
+			"mv64xxx_i2c_do_action: Invalid action: %d\n",
+			drv_data->action);
+		drv_data->rc = -EIO;
+		/* FALLTHRU */
+	case MV64XXX_I2C_ACTION_SEND_STOP:
+		drv_data->cntl_bits &= ~MV64XXX_I2C_REG_CONTROL_INTEN;
+		writel(drv_data->cntl_bits | MV64XXX_I2C_REG_CONTROL_STOP,
+			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+		drv_data->block = 0;
+		wake_up_interruptible(&drv_data->waitq);
+		break;
+	}
+
+	return;
+}
+
+static int
+mv64xxx_i2c_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct mv64xxx_i2c_data	*drv_data = dev_id;
+	u32	status;
+	long	flags;
+	int	rc = IRQ_NONE;
+
+	spin_lock_irqsave(&drv_data->lock, flags);
+	while (readl(drv_data->reg_base + MV64XXX_I2C_REG_CONTROL) &
+						MV64XXX_I2C_REG_CONTROL_IFLG) {
+		status = readl(drv_data->reg_base + MV64XXX_I2C_REG_STATUS);
+		mv64xxx_i2c_fsm(drv_data, status);
+		mv64xxx_i2c_do_action(drv_data);
+		rc = IRQ_HANDLED;
+	}
+	spin_unlock_irqrestore(&drv_data->lock, flags);
+
+	return rc;
+}
+
+/*
+ *****************************************************************************
+ *
+ *	I2C Msg Execution Routines
+ *
+ *****************************************************************************
+ */
+static void
+mv64xxx_i2c_prepare_for_io(struct mv64xxx_i2c_data *drv_data,
+	struct i2c_msg *msg)
+{
+	u32	dir = 0;
+
+	drv_data->msg = msg;
+	drv_data->byte_posn = 0;
+	drv_data->bytes_left = msg->len;
+	drv_data->rc = 0;
+	drv_data->cntl_bits = MV64XXX_I2C_REG_CONTROL_ACK |
+		MV64XXX_I2C_REG_CONTROL_INTEN | MV64XXX_I2C_REG_CONTROL_TWSIEN;
+
+	if (msg->flags & I2C_M_RD)
+		dir = 1;
+
+	if (msg->flags & I2C_M_REV_DIR_ADDR)
+		dir ^= 1;
+
+	if (msg->flags & I2C_M_TEN) {
+		drv_data->addr1 = 0xf0 | (((u32)msg->addr & 0x300) >> 7) | dir;
+		drv_data->addr2 = (u32)msg->addr & 0xff;
+	} else {
+		drv_data->addr1 = ((u32)msg->addr & 0x7f) << 1 | dir;
+		drv_data->addr2 = 0;
+	}
+
+	return;
+}
+
+static void
+mv64xxx_i2c_wait_for_completion(struct mv64xxx_i2c_data *drv_data)
+{
+	long	flags, time_left;
+	char	abort = 0;
+
+	time_left = wait_event_interruptible_timeout(drv_data->waitq,
+		!drv_data->block, msecs_to_jiffies(drv_data->adapter.timeout));
+
+	spin_lock_irqsave(&drv_data->lock, flags);
+	if (!time_left) { /* Timed out */
+		drv_data->rc = -ETIMEDOUT;
+		abort = 1;
+	} else if (time_left < 0) { /* Interrupted/Error */
+		drv_data->rc = time_left; /* errno value */
+		abort = 1;
+	}
+
+	if (abort && drv_data->block) {
+		drv_data->state = MV64XXX_I2C_STATE_ABORTING;
+		spin_unlock_irqrestore(&drv_data->lock, flags);
+
+		time_left = wait_event_timeout(drv_data->waitq,
+			!drv_data->block,
+			msecs_to_jiffies(drv_data->adapter.timeout));
+
+		if (time_left <= 0) {
+			drv_data->state = MV64XXX_I2C_STATE_IDLE;
+			dev_err(&drv_data->adapter.dev,
+				"mv64xxx: I2C bus locked\n");
+		}
+	} else
+		spin_unlock_irqrestore(&drv_data->lock, flags);
+
+	return;
+}
+
+static int
+mv64xxx_i2c_execute_msg(struct mv64xxx_i2c_data *drv_data, struct i2c_msg *msg)
+{
+	long	flags;
+
+	spin_lock_irqsave(&drv_data->lock, flags);
+	mv64xxx_i2c_prepare_for_io(drv_data, msg);
+
+	if (unlikely(msg->flags & I2C_M_NOSTART)) { /* Skip start/addr phases */
+		if (drv_data->msg->flags & I2C_M_RD) {
+			/* No action to do, wait for slave to send a byte */
+			drv_data->action = MV64XXX_I2C_ACTION_CONTINUE;
+			drv_data->state =
+				MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_DATA;
+		} else {
+			drv_data->action = MV64XXX_I2C_ACTION_SEND_DATA;
+			drv_data->state =
+				MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_ACK;
+			drv_data->bytes_left--;
+		}
+	} else {
+		drv_data->action = MV64XXX_I2C_ACTION_SEND_START;
+		drv_data->state = MV64XXX_I2C_STATE_WAITING_FOR_START_COND;
+	}
+
+	drv_data->block = 1;
+	mv64xxx_i2c_do_action(drv_data);
+	spin_unlock_irqrestore(&drv_data->lock, flags);
+
+	mv64xxx_i2c_wait_for_completion(drv_data);
+	return drv_data->rc;
+}
+
+/*
+ *****************************************************************************
+ *
+ *	I2C Core Support Routines (Interface to higher level I2C code)
+ *
+ *****************************************************************************
+ */
+static u32
+mv64xxx_i2c_functionality(struct i2c_adapter *adap)
+{
+	return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR | I2C_FUNC_SMBUS_EMUL;
+}
+
+static int
+mv64xxx_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
+{
+	struct mv64xxx_i2c_data *drv_data = i2c_get_adapdata(adap);
+	int	i, rc = 0;
+
+	for (i=0; i<num; i++)
+		if ((rc = mv64xxx_i2c_execute_msg(drv_data, &msgs[i])) != 0)
+			break;
+
+	return rc;
+}
+
+static struct i2c_algorithm mv64xxx_i2c_algo = {
+	.name = MV64XXX_I2C_CTLR_NAME " algorithm",
+	.id = I2C_ALGO_MV64XXX,
+	.master_xfer = mv64xxx_i2c_xfer,
+	.functionality = mv64xxx_i2c_functionality,
+};
+
+/*
+ *****************************************************************************
+ *
+ *	Driver Interface & Early Init Routines
+ *
+ *****************************************************************************
+ */
+static void __devinit
+mv64xxx_i2c_hw_init(struct mv64xxx_i2c_data *drv_data)
+{
+	writel(0, drv_data->reg_base + MV64XXX_I2C_REG_SOFT_RESET);
+	writel((((drv_data->freq_m & 0xf) << 3) | (drv_data->freq_n & 0x7)),
+		drv_data->reg_base + MV64XXX_I2C_REG_BAUD);
+	writel(0, drv_data->reg_base + MV64XXX_I2C_REG_SLAVE_ADDR);
+	writel(0, drv_data->reg_base + MV64XXX_I2C_REG_EXT_SLAVE_ADDR);
+	writel(MV64XXX_I2C_REG_CONTROL_TWSIEN | MV64XXX_I2C_REG_CONTROL_STOP,
+		drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+	drv_data->state = MV64XXX_I2C_STATE_IDLE;
+	return;
+}
+
+static int __devinit
+mv64xxx_i2c_map_regs(struct platform_device *pd,
+	struct mv64xxx_i2c_data *drv_data)
+{
+	struct resource	*r;
+
+	if ((r = platform_get_resource(pd, IORESOURCE_MEM, 0)) &&
+		request_mem_region(r->start, MV64XXX_I2C_REG_BLOCK_SIZE,
+			drv_data->adapter.name)) {
+
+		drv_data->reg_base = ioremap(r->start,
+			MV64XXX_I2C_REG_BLOCK_SIZE);
+		drv_data->reg_base_p = r->start;
+	} else
+		return -ENOMEM;
+
+	return 0;
+}
+
+static void __devexit
+mv64xxx_i2c_unmap_regs(struct mv64xxx_i2c_data *drv_data)
+{
+	if (drv_data->reg_base) {
+		iounmap(drv_data->reg_base);
+		release_mem_region(drv_data->reg_base_p,
+			MV64XXX_I2C_REG_BLOCK_SIZE);
+	}
+
+	drv_data->reg_base = NULL;
+	drv_data->reg_base_p = 0;
+	return;
+}
+
+static int __devinit
+mv64xxx_i2c_probe(struct device *dev)
+{
+	struct platform_device		*pd = to_platform_device(dev);
+	struct mv64xxx_i2c_data		*drv_data;
+	struct mv64xxx_i2c_pdata	*pdata = dev->platform_data;
+	int	rc;
+
+	if ((pd->id != 0) || !pdata)
+		return -ENODEV;
+
+	drv_data = kmalloc(sizeof(struct mv64xxx_i2c_data), GFP_KERNEL);
+
+	if (!drv_data)
+		return -ENOMEM;
+
+	memset(drv_data, 0, sizeof(struct mv64xxx_i2c_data));
+
+	if (mv64xxx_i2c_map_regs(pd, drv_data)) {
+		rc = -ENODEV;
+		goto exit_kfree;
+	}
+
+	strncpy(drv_data->adapter.name, MV64XXX_I2C_CTLR_NAME " adapter",
+		I2C_NAME_SIZE);
+
+	init_waitqueue_head(&drv_data->waitq);
+	spin_lock_init(&drv_data->lock);
+
+	drv_data->freq_m = pdata->freq_m;
+	drv_data->freq_n = pdata->freq_n;
+	drv_data->irq = platform_get_irq(pd, 0);
+	drv_data->adapter.id = I2C_ALGO_MV64XXX | I2C_HW_MV64XXX;
+	drv_data->adapter.algo = &mv64xxx_i2c_algo;
+	drv_data->adapter.timeout = pdata->timeout;
+	drv_data->adapter.retries = pdata->retries;
+	dev_set_drvdata(dev, drv_data);
+	i2c_set_adapdata(&drv_data->adapter, drv_data);
+
+	if (request_irq(drv_data->irq, mv64xxx_i2c_intr, 0,
+		MV64XXX_I2C_CTLR_NAME, drv_data)) {
+
+		dev_err(dev, "mv64xxx: Can't register intr handler "
+			"irq: %d\n", drv_data->irq);
+		rc = -EINVAL;
+		goto exit_unmap_regs;
+	} else if ((rc = i2c_add_adapter(&drv_data->adapter)) != 0) {
+		dev_err(dev, "mv64xxx: Can't add i2c adapter, rc: %d\n", -rc);
+		goto exit_free_irq;
+	}
+
+	mv64xxx_i2c_hw_init(drv_data);
+
+	return 0;
+
+	exit_free_irq:
+		free_irq(drv_data->irq, drv_data);
+	exit_unmap_regs:
+		mv64xxx_i2c_unmap_regs(drv_data);
+	exit_kfree:
+		kfree(drv_data);
+	return rc;
+}
+
+static int __devexit
+mv64xxx_i2c_remove(struct device *dev)
+{
+	struct mv64xxx_i2c_data		*drv_data = dev_get_drvdata(dev);
+	int	rc;
+
+	rc = i2c_del_adapter(&drv_data->adapter);
+	free_irq(drv_data->irq, drv_data);
+	mv64xxx_i2c_unmap_regs(drv_data);
+	kfree(drv_data);
+
+	return rc;
+}
+
+static struct device_driver mv64xxx_i2c_driver = {
+	.name	= MV64XXX_I2C_CTLR_NAME,
+	.bus	= &platform_bus_type,
+	.probe	= mv64xxx_i2c_probe,
+	.remove	= mv64xxx_i2c_remove,
+};
+
+static int __devinit
+mv64xxx_i2c_init(void)
+{
+	return driver_register(&mv64xxx_i2c_driver);
+}
+
+static void __devexit
+mv64xxx_i2c_exit(void)
+{
+	driver_unregister(&mv64xxx_i2c_driver);
+	return;
+}
+
+module_init(mv64xxx_i2c_init);
+module_exit(mv64xxx_i2c_exit);
+
+MODULE_AUTHOR("Mark A. Greer <mgreer@mvista.com>");
+MODULE_DESCRIPTION("Marvell mv64xxx host bridge i2c ctlr driver");
+MODULE_LICENSE("GPL");
diff -Nru a/include/linux/i2c-id.h b/include/linux/i2c-id.h
--- a/include/linux/i2c-id.h	2005-02-08 16:24:33 -07:00
+++ b/include/linux/i2c-id.h	2005-02-08 16:24:33 -07:00
@@ -311,4 +311,7 @@
 /* --- MCP107 adapter */
 #define I2C_HW_MPC107 0x00
 
+/* --- Marvell mv64xxx i2c adapter */
+#define I2C_HW_MV64XXX 0x00
+
 #endif /* LINUX_I2C_ID_H */
diff -Nru a/include/linux/mv643xx.h b/include/linux/mv643xx.h
--- a/include/linux/mv643xx.h	2005-02-08 16:24:33 -07:00
+++ b/include/linux/mv643xx.h	2005-02-08 16:24:33 -07:00
@@ -977,12 +977,9 @@
 /* I2C Registers                        */
 /****************************************/
 
-#define MV64340_I2C_SLAVE_ADDR                                      0xc000
-#define MV64340_I2C_EXTENDED_SLAVE_ADDR                             0xc010
-#define MV64340_I2C_DATA                                            0xc004
-#define MV64340_I2C_CONTROL                                         0xc008
-#define MV64340_I2C_STATUS_BAUDE_RATE                               0xc00C
-#define MV64340_I2C_SOFT_RESET                                      0xc01c
+#define MV64XXX_I2C_CTLR_NAME					"mv64xxx i2c"
+#define MV64XXX_I2C_OFFSET                                          0xc000
+#define MV64XXX_I2C_REG_BLOCK_SIZE                                  0x0020
 
 /****************************************/
 /* GPP Interface Registers              */
@@ -1083,6 +1080,14 @@
 	u8	brg_can_tune;
 	u8	brg_clk_src;
 	u32	brg_clk_freq;
+};
+
+/* i2c Platform Device, Driver Data */
+struct mv64xxx_i2c_pdata {
+	u32	freq_m;
+	u32	freq_n;
+	u32	timeout;	/* In milliseconds */
+	u32	retries;
 };
 
 #endif /* __ASM_MV64340_H */

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

* Re: [PATCH][I2C] Marvell mv64xxx i2c driver
  2005-02-08 23:27 [PATCH][I2C] Marvell mv64xxx i2c driver Mark A. Greer
@ 2005-02-09  0:01 ` Bartlomiej Zolnierkiewicz
  2005-02-09  0:32   ` Mark A. Greer
  0 siblings, 1 reply; 23+ messages in thread
From: Bartlomiej Zolnierkiewicz @ 2005-02-09  0:01 UTC (permalink / raw)
  To: Mark A. Greer; +Cc: Greg KH, LM Sensors, lkml

Hi,

just a minor thing

> +static int __devinit
> +mv64xxx_i2c_init(void)
> +{
> +       return driver_register(&mv64xxx_i2c_driver);
> +}

__init

> +static void __devexit
> +mv64xxx_i2c_exit(void)
> +{
> +       driver_unregister(&mv64xxx_i2c_driver);
> +       return;
> +}

__exit

these functions relate to module not device

> +module_init(mv64xxx_i2c_init);
> +module_exit(mv64xxx_i2c_exit);

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

* Re: [PATCH][I2C] Marvell mv64xxx i2c driver
  2005-02-09  0:01 ` Bartlomiej Zolnierkiewicz
@ 2005-02-09  0:32   ` Mark A. Greer
  2005-02-09  1:24     ` Bartlomiej Zolnierkiewicz
  0 siblings, 1 reply; 23+ messages in thread
From: Mark A. Greer @ 2005-02-09  0:32 UTC (permalink / raw)
  To: Bartlomiej Zolnierkiewicz; +Cc: Greg KH, LM Sensors, lkml

[-- Attachment #1: Type: text/plain, Size: 808 bytes --]

Bartlomiej Zolnierkiewicz wrote:

>Hi,
>
>just a minor thing
>
>  
>
>>+static int __devinit
>>+mv64xxx_i2c_init(void)
>>+{
>>+       return driver_register(&mv64xxx_i2c_driver);
>>+}
>>    
>>
>
>__init
>
>  
>
>>+static void __devexit
>>+mv64xxx_i2c_exit(void)
>>+{
>>+       driver_unregister(&mv64xxx_i2c_driver);
>>+       return;
>>+}
>>    
>>
>
>__exit
>
>these functions relate to module not device
>

Gahhh.  Thanks Bartlomiej.

Below is yet another replacement patch
--

Marvell makes a line of host bridge for PPC and MIPS systems.  On those 
bridges is an i2c controller.  This patch adds the driver for that i2c 
controller.

Please apply.

Depends on patch submitted by Jean Delvare: 
http://archives.andrew.net.au/lm-sensors/msg29405.html

Signed-off-by: Mark A. Greer <mgreer@mvista.com>
--

[-- Attachment #2: i2c_8.patch --]
[-- Type: text/plain, Size: 20528 bytes --]

diff -Nru a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
--- a/drivers/i2c/busses/Kconfig	2005-02-08 17:28:52 -07:00
+++ b/drivers/i2c/busses/Kconfig	2005-02-08 17:28:52 -07:00
@@ -486,4 +486,14 @@
 	  This driver can also be built as a module.  If so, the module
 	  will be called i2c-pca-isa.
 
+config I2C_MV64XXX
+	tristate "Marvell mv64xxx I2C Controller"
+	depends on I2C && MV64X60 && EXPERIMENTAL
+	help
+	  If you say yes to this option, support will be included for the
+	  built-in I2C interface on the Marvell 64xxx line of host bridges.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called i2c-mv64xxx.
+
 endmenu
diff -Nru a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
--- a/drivers/i2c/busses/Makefile	2005-02-08 17:28:52 -07:00
+++ b/drivers/i2c/busses/Makefile	2005-02-08 17:28:52 -07:00
@@ -21,6 +21,7 @@
 obj-$(CONFIG_I2C_IXP4XX)	+= i2c-ixp4xx.o
 obj-$(CONFIG_I2C_KEYWEST)	+= i2c-keywest.o
 obj-$(CONFIG_I2C_MPC)		+= i2c-mpc.o
+obj-$(CONFIG_I2C_MV64XXX)	+= i2c-mv64xxx.o
 obj-$(CONFIG_I2C_NFORCE2)	+= i2c-nforce2.o
 obj-$(CONFIG_I2C_PARPORT)	+= i2c-parport.o
 obj-$(CONFIG_I2C_PARPORT_LIGHT)	+= i2c-parport-light.o
diff -Nru a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/drivers/i2c/busses/i2c-mv64xxx.c	2005-02-08 17:28:52 -07:00
@@ -0,0 +1,606 @@
+/*
+ * drivers/i2c/busses/i2c-mv64xxx.c
+ * 
+ * Driver for the i2c controller on the Marvell line of host bridges for MIPS
+ * and PPC (e.g, gt642[46]0, mv643[46]0, mv644[46]0).
+ *
+ * Author: Mark A. Greer <mgreer@mvista.com>
+ *
+ * 2005 (c) MontaVista, Software, Inc.  This file is licensed under
+ * the terms of the GNU General Public License version 2.  This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/mv643xx.h>
+#include <asm/io.h>
+
+/* Register defines */
+#define	MV64XXX_I2C_REG_SLAVE_ADDR			0x00
+#define	MV64XXX_I2C_REG_DATA				0x04
+#define	MV64XXX_I2C_REG_CONTROL				0x08
+#define	MV64XXX_I2C_REG_STATUS				0x0c
+#define	MV64XXX_I2C_REG_BAUD				0x0c
+#define	MV64XXX_I2C_REG_EXT_SLAVE_ADDR			0x10
+#define	MV64XXX_I2C_REG_SOFT_RESET			0x1c
+
+#define	MV64XXX_I2C_REG_CONTROL_ACK			0x00000004
+#define	MV64XXX_I2C_REG_CONTROL_IFLG			0x00000008
+#define	MV64XXX_I2C_REG_CONTROL_STOP			0x00000010
+#define	MV64XXX_I2C_REG_CONTROL_START			0x00000020
+#define	MV64XXX_I2C_REG_CONTROL_TWSIEN			0x00000040
+#define	MV64XXX_I2C_REG_CONTROL_INTEN			0x00000080
+
+/* Ctlr status values */
+#define	MV64XXX_I2C_STATUS_BUS_ERR			0x00
+#define	MV64XXX_I2C_STATUS_MAST_START			0x08
+#define	MV64XXX_I2C_STATUS_MAST_REPEAT_START		0x10
+#define	MV64XXX_I2C_STATUS_MAST_WR_ADDR_ACK		0x18
+#define	MV64XXX_I2C_STATUS_MAST_WR_ADDR_NO_ACK		0x20
+#define	MV64XXX_I2C_STATUS_MAST_WR_ACK			0x28
+#define	MV64XXX_I2C_STATUS_MAST_WR_NO_ACK		0x30
+#define	MV64XXX_I2C_STATUS_MAST_LOST_ARB		0x38
+#define	MV64XXX_I2C_STATUS_MAST_RD_ADDR_ACK		0x40
+#define	MV64XXX_I2C_STATUS_MAST_RD_ADDR_NO_ACK		0x48
+#define	MV64XXX_I2C_STATUS_MAST_RD_DATA_ACK		0x50
+#define	MV64XXX_I2C_STATUS_MAST_RD_DATA_NO_ACK		0x58
+#define	MV64XXX_I2C_STATUS_MAST_WR_ADDR_2_ACK		0xd0
+#define	MV64XXX_I2C_STATUS_MAST_WR_ADDR_2_NO_ACK	0xd8
+#define	MV64XXX_I2C_STATUS_MAST_RD_ADDR_2_ACK		0xe0
+#define	MV64XXX_I2C_STATUS_MAST_RD_ADDR_2_NO_ACK	0xe8
+#define	MV64XXX_I2C_STATUS_NO_STATUS			0xf8
+
+/* Driver states */
+enum {
+	MV64XXX_I2C_STATE_INVALID,
+	MV64XXX_I2C_STATE_IDLE,
+	MV64XXX_I2C_STATE_WAITING_FOR_START_COND,
+	MV64XXX_I2C_STATE_WAITING_FOR_ADDR_1_ACK,
+	MV64XXX_I2C_STATE_WAITING_FOR_ADDR_2_ACK,
+	MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_ACK,
+	MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_DATA,
+	MV64XXX_I2C_STATE_ABORTING,
+};
+
+/* Driver actions */
+enum {
+	MV64XXX_I2C_ACTION_INVALID,
+	MV64XXX_I2C_ACTION_CONTINUE,
+	MV64XXX_I2C_ACTION_SEND_START,
+	MV64XXX_I2C_ACTION_SEND_ADDR_1,
+	MV64XXX_I2C_ACTION_SEND_ADDR_2,
+	MV64XXX_I2C_ACTION_SEND_DATA,
+	MV64XXX_I2C_ACTION_RCV_DATA,
+	MV64XXX_I2C_ACTION_RCV_DATA_STOP,
+	MV64XXX_I2C_ACTION_SEND_STOP,
+};
+
+struct mv64xxx_i2c_data {
+	int			irq;
+	u32			state;
+	u32			action;
+	u32			cntl_bits;
+	void __iomem		*reg_base;
+	u32			reg_base_p;
+	u32			addr1;
+	u32			addr2;
+	u32			bytes_left;
+	u32			byte_posn;
+	u32			block;
+	int			rc;
+	u32			freq_m;
+	u32			freq_n;
+	wait_queue_head_t	waitq;
+	spinlock_t		lock;
+	struct i2c_msg		*msg;
+	struct i2c_adapter	adapter;
+};
+
+/*
+ *****************************************************************************
+ *
+ *	Finite State Machine & Interrupt Routines
+ *
+ *****************************************************************************
+ */
+static void
+mv64xxx_i2c_fsm(struct mv64xxx_i2c_data *drv_data, u32 status)
+{
+	/*
+	 * If state is idle, then this is likely the remnants of an old
+	 * operation that driver has given up on or the user has killed.
+	 * If so, issue the stop condition and go to idle.
+	 */
+	if (drv_data->state == MV64XXX_I2C_STATE_IDLE) {
+		drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
+		return;
+	}
+
+	if (drv_data->state == MV64XXX_I2C_STATE_ABORTING) {
+		drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
+		drv_data->state = MV64XXX_I2C_STATE_IDLE;
+		return;
+	}
+
+	/* The status from the ctlr [mostly] tells us what to do next */
+	switch (status) {
+	/* Start condition interrupt */
+	case MV64XXX_I2C_STATUS_MAST_START: /* 0x08 */
+	case MV64XXX_I2C_STATUS_MAST_REPEAT_START: /* 0x10 */
+		drv_data->action = MV64XXX_I2C_ACTION_SEND_ADDR_1;
+		drv_data->state = MV64XXX_I2C_STATE_WAITING_FOR_ADDR_1_ACK;
+		break;
+
+	/* Performing a write */
+	case MV64XXX_I2C_STATUS_MAST_WR_ADDR_ACK: /* 0x18 */
+		if (drv_data->msg->flags & I2C_M_TEN) {
+			drv_data->action = MV64XXX_I2C_ACTION_SEND_ADDR_2;
+			drv_data->state =
+				MV64XXX_I2C_STATE_WAITING_FOR_ADDR_2_ACK;
+			break;
+		}
+		/* FALLTHRU */
+	case MV64XXX_I2C_STATUS_MAST_WR_ADDR_2_ACK: /* 0xd0 */
+	case MV64XXX_I2C_STATUS_MAST_WR_ACK: /* 0x28 */
+		if (drv_data->bytes_left > 0) {
+			drv_data->action = MV64XXX_I2C_ACTION_SEND_DATA;
+			drv_data->state =
+				MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_ACK;
+			drv_data->bytes_left--;
+		} else {
+			drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
+			drv_data->state = MV64XXX_I2C_STATE_IDLE;
+		}
+		break;
+
+	/* Performing a read */
+	case MV64XXX_I2C_STATUS_MAST_RD_ADDR_ACK: /* 40 */
+		if (drv_data->msg->flags & I2C_M_TEN) {
+			drv_data->action = MV64XXX_I2C_ACTION_SEND_ADDR_2;
+			drv_data->state =
+				MV64XXX_I2C_STATE_WAITING_FOR_ADDR_2_ACK;
+			break;
+		}
+		/* FALLTHRU */
+	case MV64XXX_I2C_STATUS_MAST_RD_ADDR_2_ACK: /* 0xe0 */
+		if (drv_data->bytes_left == 0) {
+			drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
+			drv_data->state = MV64XXX_I2C_STATE_IDLE;
+			break;
+		}
+		/* FALLTHRU */
+	case MV64XXX_I2C_STATUS_MAST_RD_DATA_ACK: /* 0x50 */
+		if (status != MV64XXX_I2C_STATUS_MAST_RD_DATA_ACK)
+			drv_data->action = MV64XXX_I2C_ACTION_CONTINUE;
+		else {
+			drv_data->action = MV64XXX_I2C_ACTION_RCV_DATA;
+			drv_data->bytes_left--;
+		}
+		drv_data->state = MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_DATA;
+
+		if (drv_data->bytes_left == 1)
+			drv_data->cntl_bits &= ~MV64XXX_I2C_REG_CONTROL_ACK;
+		break;
+
+	case MV64XXX_I2C_STATUS_MAST_RD_DATA_NO_ACK: /* 0x58 */
+		drv_data->action = MV64XXX_I2C_ACTION_RCV_DATA_STOP;
+		drv_data->state = MV64XXX_I2C_STATE_IDLE;
+		break;
+
+	case MV64XXX_I2C_STATUS_MAST_WR_ADDR_NO_ACK: /* 0x20 */
+	case MV64XXX_I2C_STATUS_MAST_WR_NO_ACK: /* 30 */
+	case MV64XXX_I2C_STATUS_MAST_RD_ADDR_NO_ACK: /* 48 */
+		/* Doesn't seem to be a device at other end */
+		drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
+		drv_data->state = MV64XXX_I2C_STATE_IDLE;
+		drv_data->rc = -ENODEV;
+		break;
+
+	default:
+		dev_err(&drv_data->adapter.dev,
+			"mv64xxx_i2c_fsm: Ctlr Error -- state: 0x%x, "
+			"status: 0x%x, addr: 0x%x, flags: 0x%x\n",
+			 drv_data->state, status, drv_data->msg->addr,
+			 drv_data->msg->flags);
+		drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
+		drv_data->state = MV64XXX_I2C_STATE_IDLE;
+		drv_data->rc = -EIO;
+	}
+
+	return;
+}
+
+static void
+mv64xxx_i2c_do_action(struct mv64xxx_i2c_data *drv_data)
+{
+	switch(drv_data->action) {
+	case MV64XXX_I2C_ACTION_CONTINUE:
+		writel(drv_data->cntl_bits,
+			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+		break;
+
+	case MV64XXX_I2C_ACTION_SEND_START:
+		writel(drv_data->cntl_bits | MV64XXX_I2C_REG_CONTROL_START,
+			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+		break;
+
+	case MV64XXX_I2C_ACTION_SEND_ADDR_1:
+		writel(drv_data->addr1,
+			drv_data->reg_base + MV64XXX_I2C_REG_DATA);
+		writel(drv_data->cntl_bits,
+			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+		break;
+
+	case MV64XXX_I2C_ACTION_SEND_ADDR_2:
+		writel(drv_data->addr2,
+			drv_data->reg_base + MV64XXX_I2C_REG_DATA);
+		writel(drv_data->cntl_bits,
+			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+		break;
+
+	case MV64XXX_I2C_ACTION_SEND_DATA:
+		writel(drv_data->msg->buf[drv_data->byte_posn++],
+			drv_data->reg_base + MV64XXX_I2C_REG_DATA);
+		writel(drv_data->cntl_bits,
+			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+		break;
+
+	case MV64XXX_I2C_ACTION_RCV_DATA:
+		drv_data->msg->buf[drv_data->byte_posn++] =
+			readl(drv_data->reg_base + MV64XXX_I2C_REG_DATA);
+		writel(drv_data->cntl_bits,
+			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+		break;
+
+	case MV64XXX_I2C_ACTION_RCV_DATA_STOP:
+		drv_data->msg->buf[drv_data->byte_posn++] =
+			readl(drv_data->reg_base + MV64XXX_I2C_REG_DATA);
+		drv_data->cntl_bits &= ~MV64XXX_I2C_REG_CONTROL_INTEN;
+		writel(drv_data->cntl_bits | MV64XXX_I2C_REG_CONTROL_STOP,
+			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+		drv_data->block = 0;
+		wake_up_interruptible(&drv_data->waitq);
+		break;
+
+	case MV64XXX_I2C_ACTION_INVALID:
+	default:
+		dev_err(&drv_data->adapter.dev,
+			"mv64xxx_i2c_do_action: Invalid action: %d\n",
+			drv_data->action);
+		drv_data->rc = -EIO;
+		/* FALLTHRU */
+	case MV64XXX_I2C_ACTION_SEND_STOP:
+		drv_data->cntl_bits &= ~MV64XXX_I2C_REG_CONTROL_INTEN;
+		writel(drv_data->cntl_bits | MV64XXX_I2C_REG_CONTROL_STOP,
+			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+		drv_data->block = 0;
+		wake_up_interruptible(&drv_data->waitq);
+		break;
+	}
+
+	return;
+}
+
+static int
+mv64xxx_i2c_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct mv64xxx_i2c_data	*drv_data = dev_id;
+	u32	status;
+	long	flags;
+	int	rc = IRQ_NONE;
+
+	spin_lock_irqsave(&drv_data->lock, flags);
+	while (readl(drv_data->reg_base + MV64XXX_I2C_REG_CONTROL) &
+						MV64XXX_I2C_REG_CONTROL_IFLG) {
+		status = readl(drv_data->reg_base + MV64XXX_I2C_REG_STATUS);
+		mv64xxx_i2c_fsm(drv_data, status);
+		mv64xxx_i2c_do_action(drv_data);
+		rc = IRQ_HANDLED;
+	}
+	spin_unlock_irqrestore(&drv_data->lock, flags);
+
+	return rc;
+}
+
+/*
+ *****************************************************************************
+ *
+ *	I2C Msg Execution Routines
+ *
+ *****************************************************************************
+ */
+static void
+mv64xxx_i2c_prepare_for_io(struct mv64xxx_i2c_data *drv_data,
+	struct i2c_msg *msg)
+{
+	u32	dir = 0;
+
+	drv_data->msg = msg;
+	drv_data->byte_posn = 0;
+	drv_data->bytes_left = msg->len;
+	drv_data->rc = 0;
+	drv_data->cntl_bits = MV64XXX_I2C_REG_CONTROL_ACK |
+		MV64XXX_I2C_REG_CONTROL_INTEN | MV64XXX_I2C_REG_CONTROL_TWSIEN;
+
+	if (msg->flags & I2C_M_RD)
+		dir = 1;
+
+	if (msg->flags & I2C_M_REV_DIR_ADDR)
+		dir ^= 1;
+
+	if (msg->flags & I2C_M_TEN) {
+		drv_data->addr1 = 0xf0 | (((u32)msg->addr & 0x300) >> 7) | dir;
+		drv_data->addr2 = (u32)msg->addr & 0xff;
+	} else {
+		drv_data->addr1 = ((u32)msg->addr & 0x7f) << 1 | dir;
+		drv_data->addr2 = 0;
+	}
+
+	return;
+}
+
+static void
+mv64xxx_i2c_wait_for_completion(struct mv64xxx_i2c_data *drv_data)
+{
+	long	flags, time_left;
+	char	abort = 0;
+
+	time_left = wait_event_interruptible_timeout(drv_data->waitq,
+		!drv_data->block, msecs_to_jiffies(drv_data->adapter.timeout));
+
+	spin_lock_irqsave(&drv_data->lock, flags);
+	if (!time_left) { /* Timed out */
+		drv_data->rc = -ETIMEDOUT;
+		abort = 1;
+	} else if (time_left < 0) { /* Interrupted/Error */
+		drv_data->rc = time_left; /* errno value */
+		abort = 1;
+	}
+
+	if (abort && drv_data->block) {
+		drv_data->state = MV64XXX_I2C_STATE_ABORTING;
+		spin_unlock_irqrestore(&drv_data->lock, flags);
+
+		time_left = wait_event_timeout(drv_data->waitq,
+			!drv_data->block,
+			msecs_to_jiffies(drv_data->adapter.timeout));
+
+		if (time_left <= 0) {
+			drv_data->state = MV64XXX_I2C_STATE_IDLE;
+			dev_err(&drv_data->adapter.dev,
+				"mv64xxx: I2C bus locked\n");
+		}
+	} else
+		spin_unlock_irqrestore(&drv_data->lock, flags);
+
+	return;
+}
+
+static int
+mv64xxx_i2c_execute_msg(struct mv64xxx_i2c_data *drv_data, struct i2c_msg *msg)
+{
+	long	flags;
+
+	spin_lock_irqsave(&drv_data->lock, flags);
+	mv64xxx_i2c_prepare_for_io(drv_data, msg);
+
+	if (unlikely(msg->flags & I2C_M_NOSTART)) { /* Skip start/addr phases */
+		if (drv_data->msg->flags & I2C_M_RD) {
+			/* No action to do, wait for slave to send a byte */
+			drv_data->action = MV64XXX_I2C_ACTION_CONTINUE;
+			drv_data->state =
+				MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_DATA;
+		} else {
+			drv_data->action = MV64XXX_I2C_ACTION_SEND_DATA;
+			drv_data->state =
+				MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_ACK;
+			drv_data->bytes_left--;
+		}
+	} else {
+		drv_data->action = MV64XXX_I2C_ACTION_SEND_START;
+		drv_data->state = MV64XXX_I2C_STATE_WAITING_FOR_START_COND;
+	}
+
+	drv_data->block = 1;
+	mv64xxx_i2c_do_action(drv_data);
+	spin_unlock_irqrestore(&drv_data->lock, flags);
+
+	mv64xxx_i2c_wait_for_completion(drv_data);
+	return drv_data->rc;
+}
+
+/*
+ *****************************************************************************
+ *
+ *	I2C Core Support Routines (Interface to higher level I2C code)
+ *
+ *****************************************************************************
+ */
+static u32
+mv64xxx_i2c_functionality(struct i2c_adapter *adap)
+{
+	return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR | I2C_FUNC_SMBUS_EMUL;
+}
+
+static int
+mv64xxx_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
+{
+	struct mv64xxx_i2c_data *drv_data = i2c_get_adapdata(adap);
+	int	i, rc = 0;
+
+	for (i=0; i<num; i++)
+		if ((rc = mv64xxx_i2c_execute_msg(drv_data, &msgs[i])) != 0)
+			break;
+
+	return rc;
+}
+
+static struct i2c_algorithm mv64xxx_i2c_algo = {
+	.name = MV64XXX_I2C_CTLR_NAME " algorithm",
+	.id = I2C_ALGO_MV64XXX,
+	.master_xfer = mv64xxx_i2c_xfer,
+	.functionality = mv64xxx_i2c_functionality,
+};
+
+/*
+ *****************************************************************************
+ *
+ *	Driver Interface & Early Init Routines
+ *
+ *****************************************************************************
+ */
+static void __init
+mv64xxx_i2c_hw_init(struct mv64xxx_i2c_data *drv_data)
+{
+	writel(0, drv_data->reg_base + MV64XXX_I2C_REG_SOFT_RESET);
+	writel((((drv_data->freq_m & 0xf) << 3) | (drv_data->freq_n & 0x7)),
+		drv_data->reg_base + MV64XXX_I2C_REG_BAUD);
+	writel(0, drv_data->reg_base + MV64XXX_I2C_REG_SLAVE_ADDR);
+	writel(0, drv_data->reg_base + MV64XXX_I2C_REG_EXT_SLAVE_ADDR);
+	writel(MV64XXX_I2C_REG_CONTROL_TWSIEN | MV64XXX_I2C_REG_CONTROL_STOP,
+		drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+	drv_data->state = MV64XXX_I2C_STATE_IDLE;
+	return;
+}
+
+static int __init
+mv64xxx_i2c_map_regs(struct platform_device *pd,
+	struct mv64xxx_i2c_data *drv_data)
+{
+	struct resource	*r;
+
+	if ((r = platform_get_resource(pd, IORESOURCE_MEM, 0)) &&
+		request_mem_region(r->start, MV64XXX_I2C_REG_BLOCK_SIZE,
+			drv_data->adapter.name)) {
+
+		drv_data->reg_base = ioremap(r->start,
+			MV64XXX_I2C_REG_BLOCK_SIZE);
+		drv_data->reg_base_p = r->start;
+	} else
+		return -ENOMEM;
+
+	return 0;
+}
+
+static void __exit
+mv64xxx_i2c_unmap_regs(struct mv64xxx_i2c_data *drv_data)
+{
+	if (drv_data->reg_base) {
+		iounmap(drv_data->reg_base);
+		release_mem_region(drv_data->reg_base_p,
+			MV64XXX_I2C_REG_BLOCK_SIZE);
+	}
+
+	drv_data->reg_base = NULL;
+	drv_data->reg_base_p = 0;
+	return;
+}
+
+static int __init
+mv64xxx_i2c_probe(struct device *dev)
+{
+	struct platform_device		*pd = to_platform_device(dev);
+	struct mv64xxx_i2c_data		*drv_data;
+	struct mv64xxx_i2c_pdata	*pdata = dev->platform_data;
+	int	rc;
+
+	if ((pd->id != 0) || !pdata)
+		return -ENODEV;
+
+	drv_data = kmalloc(sizeof(struct mv64xxx_i2c_data), GFP_KERNEL);
+
+	if (!drv_data)
+		return -ENOMEM;
+
+	memset(drv_data, 0, sizeof(struct mv64xxx_i2c_data));
+
+	if (mv64xxx_i2c_map_regs(pd, drv_data)) {
+		rc = -ENODEV;
+		goto exit_kfree;
+	}
+
+	strncpy(drv_data->adapter.name, MV64XXX_I2C_CTLR_NAME " adapter",
+		I2C_NAME_SIZE);
+
+	init_waitqueue_head(&drv_data->waitq);
+	spin_lock_init(&drv_data->lock);
+
+	drv_data->freq_m = pdata->freq_m;
+	drv_data->freq_n = pdata->freq_n;
+	drv_data->irq = platform_get_irq(pd, 0);
+	drv_data->adapter.id = I2C_ALGO_MV64XXX | I2C_HW_MV64XXX;
+	drv_data->adapter.algo = &mv64xxx_i2c_algo;
+	drv_data->adapter.timeout = pdata->timeout;
+	drv_data->adapter.retries = pdata->retries;
+	dev_set_drvdata(dev, drv_data);
+	i2c_set_adapdata(&drv_data->adapter, drv_data);
+
+	if (request_irq(drv_data->irq, mv64xxx_i2c_intr, 0,
+		MV64XXX_I2C_CTLR_NAME, drv_data)) {
+
+		dev_err(dev, "mv64xxx: Can't register intr handler "
+			"irq: %d\n", drv_data->irq);
+		rc = -EINVAL;
+		goto exit_unmap_regs;
+	} else if ((rc = i2c_add_adapter(&drv_data->adapter)) != 0) {
+		dev_err(dev, "mv64xxx: Can't add i2c adapter, rc: %d\n", -rc);
+		goto exit_free_irq;
+	}
+
+	mv64xxx_i2c_hw_init(drv_data);
+
+	return 0;
+
+	exit_free_irq:
+		free_irq(drv_data->irq, drv_data);
+	exit_unmap_regs:
+		mv64xxx_i2c_unmap_regs(drv_data);
+	exit_kfree:
+		kfree(drv_data);
+	return rc;
+}
+
+static int __exit
+mv64xxx_i2c_remove(struct device *dev)
+{
+	struct mv64xxx_i2c_data		*drv_data = dev_get_drvdata(dev);
+	int	rc;
+
+	rc = i2c_del_adapter(&drv_data->adapter);
+	free_irq(drv_data->irq, drv_data);
+	mv64xxx_i2c_unmap_regs(drv_data);
+	kfree(drv_data);
+
+	return rc;
+}
+
+static struct device_driver mv64xxx_i2c_driver = {
+	.name	= MV64XXX_I2C_CTLR_NAME,
+	.bus	= &platform_bus_type,
+	.probe	= mv64xxx_i2c_probe,
+	.remove	= mv64xxx_i2c_remove,
+};
+
+static int __init
+mv64xxx_i2c_init(void)
+{
+	return driver_register(&mv64xxx_i2c_driver);
+}
+
+static void __exit
+mv64xxx_i2c_exit(void)
+{
+	driver_unregister(&mv64xxx_i2c_driver);
+	return;
+}
+
+module_init(mv64xxx_i2c_init);
+module_exit(mv64xxx_i2c_exit);
+
+MODULE_AUTHOR("Mark A. Greer <mgreer@mvista.com>");
+MODULE_DESCRIPTION("Marvell mv64xxx host bridge i2c ctlr driver");
+MODULE_LICENSE("GPL");
diff -Nru a/include/linux/i2c-id.h b/include/linux/i2c-id.h
--- a/include/linux/i2c-id.h	2005-02-08 17:28:52 -07:00
+++ b/include/linux/i2c-id.h	2005-02-08 17:28:52 -07:00
@@ -311,4 +311,7 @@
 /* --- MCP107 adapter */
 #define I2C_HW_MPC107 0x00
 
+/* --- Marvell mv64xxx i2c adapter */
+#define I2C_HW_MV64XXX 0x00
+
 #endif /* LINUX_I2C_ID_H */
diff -Nru a/include/linux/mv643xx.h b/include/linux/mv643xx.h
--- a/include/linux/mv643xx.h	2005-02-08 17:28:52 -07:00
+++ b/include/linux/mv643xx.h	2005-02-08 17:28:52 -07:00
@@ -977,12 +977,9 @@
 /* I2C Registers                        */
 /****************************************/
 
-#define MV64340_I2C_SLAVE_ADDR                                      0xc000
-#define MV64340_I2C_EXTENDED_SLAVE_ADDR                             0xc010
-#define MV64340_I2C_DATA                                            0xc004
-#define MV64340_I2C_CONTROL                                         0xc008
-#define MV64340_I2C_STATUS_BAUDE_RATE                               0xc00C
-#define MV64340_I2C_SOFT_RESET                                      0xc01c
+#define MV64XXX_I2C_CTLR_NAME					"mv64xxx i2c"
+#define MV64XXX_I2C_OFFSET                                          0xc000
+#define MV64XXX_I2C_REG_BLOCK_SIZE                                  0x0020
 
 /****************************************/
 /* GPP Interface Registers              */
@@ -1083,6 +1080,14 @@
 	u8	brg_can_tune;
 	u8	brg_clk_src;
 	u32	brg_clk_freq;
+};
+
+/* i2c Platform Device, Driver Data */
+struct mv64xxx_i2c_pdata {
+	u32	freq_m;
+	u32	freq_n;
+	u32	timeout;	/* In milliseconds */
+	u32	retries;
 };
 
 #endif /* __ASM_MV64340_H */

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

* Re: [PATCH][I2C] Marvell mv64xxx i2c driver
  2005-02-09  0:32   ` Mark A. Greer
@ 2005-02-09  1:24     ` Bartlomiej Zolnierkiewicz
  2005-02-09 21:33       ` Mark A. Greer
  0 siblings, 1 reply; 23+ messages in thread
From: Bartlomiej Zolnierkiewicz @ 2005-02-09  1:24 UTC (permalink / raw)
  To: Mark A. Greer; +Cc: Greg KH, LM Sensors, lkml

On Tue, 08 Feb 2005 17:32:11 -0700, Mark A. Greer <mgreer@mvista.com> wrote:
> Bartlomiej Zolnierkiewicz wrote:
> 
> >Hi,
> >
> >just a minor thing
> >
> >
> >
> >>+static int __devinit
> >>+mv64xxx_i2c_init(void)
> >>+{
> >>+       return driver_register(&mv64xxx_i2c_driver);
> >>+}
> >>
> >>
> >
> >__init
> >
> >
> >
> >>+static void __devexit
> >>+mv64xxx_i2c_exit(void)
> >>+{
> >>+       driver_unregister(&mv64xxx_i2c_driver);
> >>+       return;
> >>+}
> >>
> >>
> >
> >__exit
> >
> >these functions relate to module not device
> >
> 
> Gahhh.  Thanks Bartlomiej.
> 
> Below is yet another replacement patch

Thanks, I see that you did global replacement of __devinit
by __init and __devexit by __exit - it seems correct *only* if:
- there can be only one i2c controller in the system
- there can be only one host bridge in the system
- i2c core calls ->probe only once during driver init
  and ->remove only once during driver exit

If all conditions are really true some comment about
this in the code would still be be nice.

While at it more silly, minor nitpicking ;)

> +static void
> +mv64xxx_i2c_wait_for_completion(struct mv64xxx_i2c_data *drv_data)
> +{
> +       long    flags, time_left;

'flags' are of 'unsigned long' not 'long' type

> +       char    abort = 0;
> +
> +       time_left = wait_event_interruptible_timeout(drv_data->waitq,
> +               !drv_data->block, msecs_to_jiffies(drv_data->adapter.timeout));
> +
> +       spin_lock_irqsave(&drv_data->lock, flags);
> +       if (!time_left) { /* Timed out */
> +               drv_data->rc = -ETIMEDOUT;
> +               abort = 1;
> +       } else if (time_left < 0) { /* Interrupted/Error */
> +               drv_data->rc = time_left; /* errno value */
> +               abort = 1;
> +       }
> +
> +       if (abort && drv_data->block) {
> +               drv_data->state = MV64XXX_I2C_STATE_ABORTING;
> +               spin_unlock_irqrestore(&drv_data->lock, flags);
> +
> +               time_left = wait_event_timeout(drv_data->waitq,
> +                       !drv_data->block,
> +                       msecs_to_jiffies(drv_data->adapter.timeout));
> +
> +               if (time_left <= 0) {
> +                       drv_data->state = MV64XXX_I2C_STATE_IDLE;
> +                       dev_err(&drv_data->adapter.dev,
> +                               "mv64xxx: I2C bus locked\n");
> +               }
> +       } else
> +               spin_unlock_irqrestore(&drv_data->lock, flags);
> +
> +       return;

there is no need for explicit return in void functions

[ These two comments also apply to other code in the driver. ]

Thanks,
Bartlomiej

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

* Re: [PATCH][I2C] Marvell mv64xxx i2c driver
  2005-02-09  1:24     ` Bartlomiej Zolnierkiewicz
@ 2005-02-09 21:33       ` Mark A. Greer
  2005-02-17 22:25         ` Greg KH
  0 siblings, 1 reply; 23+ messages in thread
From: Mark A. Greer @ 2005-02-09 21:33 UTC (permalink / raw)
  To: Bartlomiej Zolnierkiewicz; +Cc: Greg KH, LM Sensors, lkml

[-- Attachment #1: Type: text/plain, Size: 1450 bytes --]

Bartlomiej Zolnierkiewicz wrote:

>Thanks, I see that you did global replacement of __devinit
>by __init and __devexit by __exit - it seems correct *only* if:
>- there can be only one i2c controller in the system
>- there can be only one host bridge in the system
>- i2c core calls ->probe only once during driver init
>  and ->remove only once during driver exit
>
>If all conditions are really true some comment about
>this in the code would still be be nice.
>

You're right.  The 'dev' is back except on the module_init/exit routines.

>While at it more silly, minor nitpicking ;)
>
>  
>
>>+static void
>>+mv64xxx_i2c_wait_for_completion(struct mv64xxx_i2c_data *drv_data)
>>+{
>>+       long    flags, time_left;
>>    
>>
>
>'flags' are of 'unsigned long' not 'long' type
>

Fixed.

>there is no need for explicit return in void functions
>

I can't find any definitive policy on this.  I kind of like the explicit 
return, I don't know why.  I've had others make the same comment, 
though, so I'll remove them since it obviously bothers people.

Attached is a replacement patch.

Thanks again, Bartlomiej.

Mark
--
Marvell makes a line of host bridge for PPC and MIPS systems.  On those 
bridges is an i2c controller.  This patch adds the driver for that i2c 
controller.

Please apply.

Depends on patch submitted by Jean Delvare: 
http://archives.andrew.net.au/lm-sensors/msg29405.html

Signed-off-by: Mark A. Greer <mgreer@mvista.com>
-- 

[-- Attachment #2: i2c_9.patch --]
[-- Type: text/plain, Size: 20503 bytes --]

diff -Nru a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
--- a/drivers/i2c/busses/Kconfig	2005-02-09 14:32:24 -07:00
+++ b/drivers/i2c/busses/Kconfig	2005-02-09 14:32:24 -07:00
@@ -486,4 +486,14 @@
 	  This driver can also be built as a module.  If so, the module
 	  will be called i2c-pca-isa.
 
+config I2C_MV64XXX
+	tristate "Marvell mv64xxx I2C Controller"
+	depends on I2C && MV64X60 && EXPERIMENTAL
+	help
+	  If you say yes to this option, support will be included for the
+	  built-in I2C interface on the Marvell 64xxx line of host bridges.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called i2c-mv64xxx.
+
 endmenu
diff -Nru a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
--- a/drivers/i2c/busses/Makefile	2005-02-09 14:32:24 -07:00
+++ b/drivers/i2c/busses/Makefile	2005-02-09 14:32:24 -07:00
@@ -21,6 +21,7 @@
 obj-$(CONFIG_I2C_IXP4XX)	+= i2c-ixp4xx.o
 obj-$(CONFIG_I2C_KEYWEST)	+= i2c-keywest.o
 obj-$(CONFIG_I2C_MPC)		+= i2c-mpc.o
+obj-$(CONFIG_I2C_MV64XXX)	+= i2c-mv64xxx.o
 obj-$(CONFIG_I2C_NFORCE2)	+= i2c-nforce2.o
 obj-$(CONFIG_I2C_PARPORT)	+= i2c-parport.o
 obj-$(CONFIG_I2C_PARPORT_LIGHT)	+= i2c-parport-light.o
diff -Nru a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/drivers/i2c/busses/i2c-mv64xxx.c	2005-02-09 14:32:24 -07:00
@@ -0,0 +1,596 @@
+/*
+ * drivers/i2c/busses/i2c-mv64xxx.c
+ * 
+ * Driver for the i2c controller on the Marvell line of host bridges for MIPS
+ * and PPC (e.g, gt642[46]0, mv643[46]0, mv644[46]0).
+ *
+ * Author: Mark A. Greer <mgreer@mvista.com>
+ *
+ * 2005 (c) MontaVista, Software, Inc.  This file is licensed under
+ * the terms of the GNU General Public License version 2.  This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/mv643xx.h>
+#include <asm/io.h>
+
+/* Register defines */
+#define	MV64XXX_I2C_REG_SLAVE_ADDR			0x00
+#define	MV64XXX_I2C_REG_DATA				0x04
+#define	MV64XXX_I2C_REG_CONTROL				0x08
+#define	MV64XXX_I2C_REG_STATUS				0x0c
+#define	MV64XXX_I2C_REG_BAUD				0x0c
+#define	MV64XXX_I2C_REG_EXT_SLAVE_ADDR			0x10
+#define	MV64XXX_I2C_REG_SOFT_RESET			0x1c
+
+#define	MV64XXX_I2C_REG_CONTROL_ACK			0x00000004
+#define	MV64XXX_I2C_REG_CONTROL_IFLG			0x00000008
+#define	MV64XXX_I2C_REG_CONTROL_STOP			0x00000010
+#define	MV64XXX_I2C_REG_CONTROL_START			0x00000020
+#define	MV64XXX_I2C_REG_CONTROL_TWSIEN			0x00000040
+#define	MV64XXX_I2C_REG_CONTROL_INTEN			0x00000080
+
+/* Ctlr status values */
+#define	MV64XXX_I2C_STATUS_BUS_ERR			0x00
+#define	MV64XXX_I2C_STATUS_MAST_START			0x08
+#define	MV64XXX_I2C_STATUS_MAST_REPEAT_START		0x10
+#define	MV64XXX_I2C_STATUS_MAST_WR_ADDR_ACK		0x18
+#define	MV64XXX_I2C_STATUS_MAST_WR_ADDR_NO_ACK		0x20
+#define	MV64XXX_I2C_STATUS_MAST_WR_ACK			0x28
+#define	MV64XXX_I2C_STATUS_MAST_WR_NO_ACK		0x30
+#define	MV64XXX_I2C_STATUS_MAST_LOST_ARB		0x38
+#define	MV64XXX_I2C_STATUS_MAST_RD_ADDR_ACK		0x40
+#define	MV64XXX_I2C_STATUS_MAST_RD_ADDR_NO_ACK		0x48
+#define	MV64XXX_I2C_STATUS_MAST_RD_DATA_ACK		0x50
+#define	MV64XXX_I2C_STATUS_MAST_RD_DATA_NO_ACK		0x58
+#define	MV64XXX_I2C_STATUS_MAST_WR_ADDR_2_ACK		0xd0
+#define	MV64XXX_I2C_STATUS_MAST_WR_ADDR_2_NO_ACK	0xd8
+#define	MV64XXX_I2C_STATUS_MAST_RD_ADDR_2_ACK		0xe0
+#define	MV64XXX_I2C_STATUS_MAST_RD_ADDR_2_NO_ACK	0xe8
+#define	MV64XXX_I2C_STATUS_NO_STATUS			0xf8
+
+/* Driver states */
+enum {
+	MV64XXX_I2C_STATE_INVALID,
+	MV64XXX_I2C_STATE_IDLE,
+	MV64XXX_I2C_STATE_WAITING_FOR_START_COND,
+	MV64XXX_I2C_STATE_WAITING_FOR_ADDR_1_ACK,
+	MV64XXX_I2C_STATE_WAITING_FOR_ADDR_2_ACK,
+	MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_ACK,
+	MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_DATA,
+	MV64XXX_I2C_STATE_ABORTING,
+};
+
+/* Driver actions */
+enum {
+	MV64XXX_I2C_ACTION_INVALID,
+	MV64XXX_I2C_ACTION_CONTINUE,
+	MV64XXX_I2C_ACTION_SEND_START,
+	MV64XXX_I2C_ACTION_SEND_ADDR_1,
+	MV64XXX_I2C_ACTION_SEND_ADDR_2,
+	MV64XXX_I2C_ACTION_SEND_DATA,
+	MV64XXX_I2C_ACTION_RCV_DATA,
+	MV64XXX_I2C_ACTION_RCV_DATA_STOP,
+	MV64XXX_I2C_ACTION_SEND_STOP,
+};
+
+struct mv64xxx_i2c_data {
+	int			irq;
+	u32			state;
+	u32			action;
+	u32			cntl_bits;
+	void __iomem		*reg_base;
+	u32			reg_base_p;
+	u32			addr1;
+	u32			addr2;
+	u32			bytes_left;
+	u32			byte_posn;
+	u32			block;
+	int			rc;
+	u32			freq_m;
+	u32			freq_n;
+	wait_queue_head_t	waitq;
+	spinlock_t		lock;
+	struct i2c_msg		*msg;
+	struct i2c_adapter	adapter;
+};
+
+/*
+ *****************************************************************************
+ *
+ *	Finite State Machine & Interrupt Routines
+ *
+ *****************************************************************************
+ */
+static void
+mv64xxx_i2c_fsm(struct mv64xxx_i2c_data *drv_data, u32 status)
+{
+	/*
+	 * If state is idle, then this is likely the remnants of an old
+	 * operation that driver has given up on or the user has killed.
+	 * If so, issue the stop condition and go to idle.
+	 */
+	if (drv_data->state == MV64XXX_I2C_STATE_IDLE) {
+		drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
+		return;
+	}
+
+	if (drv_data->state == MV64XXX_I2C_STATE_ABORTING) {
+		drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
+		drv_data->state = MV64XXX_I2C_STATE_IDLE;
+		return;
+	}
+
+	/* The status from the ctlr [mostly] tells us what to do next */
+	switch (status) {
+	/* Start condition interrupt */
+	case MV64XXX_I2C_STATUS_MAST_START: /* 0x08 */
+	case MV64XXX_I2C_STATUS_MAST_REPEAT_START: /* 0x10 */
+		drv_data->action = MV64XXX_I2C_ACTION_SEND_ADDR_1;
+		drv_data->state = MV64XXX_I2C_STATE_WAITING_FOR_ADDR_1_ACK;
+		break;
+
+	/* Performing a write */
+	case MV64XXX_I2C_STATUS_MAST_WR_ADDR_ACK: /* 0x18 */
+		if (drv_data->msg->flags & I2C_M_TEN) {
+			drv_data->action = MV64XXX_I2C_ACTION_SEND_ADDR_2;
+			drv_data->state =
+				MV64XXX_I2C_STATE_WAITING_FOR_ADDR_2_ACK;
+			break;
+		}
+		/* FALLTHRU */
+	case MV64XXX_I2C_STATUS_MAST_WR_ADDR_2_ACK: /* 0xd0 */
+	case MV64XXX_I2C_STATUS_MAST_WR_ACK: /* 0x28 */
+		if (drv_data->bytes_left > 0) {
+			drv_data->action = MV64XXX_I2C_ACTION_SEND_DATA;
+			drv_data->state =
+				MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_ACK;
+			drv_data->bytes_left--;
+		} else {
+			drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
+			drv_data->state = MV64XXX_I2C_STATE_IDLE;
+		}
+		break;
+
+	/* Performing a read */
+	case MV64XXX_I2C_STATUS_MAST_RD_ADDR_ACK: /* 40 */
+		if (drv_data->msg->flags & I2C_M_TEN) {
+			drv_data->action = MV64XXX_I2C_ACTION_SEND_ADDR_2;
+			drv_data->state =
+				MV64XXX_I2C_STATE_WAITING_FOR_ADDR_2_ACK;
+			break;
+		}
+		/* FALLTHRU */
+	case MV64XXX_I2C_STATUS_MAST_RD_ADDR_2_ACK: /* 0xe0 */
+		if (drv_data->bytes_left == 0) {
+			drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
+			drv_data->state = MV64XXX_I2C_STATE_IDLE;
+			break;
+		}
+		/* FALLTHRU */
+	case MV64XXX_I2C_STATUS_MAST_RD_DATA_ACK: /* 0x50 */
+		if (status != MV64XXX_I2C_STATUS_MAST_RD_DATA_ACK)
+			drv_data->action = MV64XXX_I2C_ACTION_CONTINUE;
+		else {
+			drv_data->action = MV64XXX_I2C_ACTION_RCV_DATA;
+			drv_data->bytes_left--;
+		}
+		drv_data->state = MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_DATA;
+
+		if (drv_data->bytes_left == 1)
+			drv_data->cntl_bits &= ~MV64XXX_I2C_REG_CONTROL_ACK;
+		break;
+
+	case MV64XXX_I2C_STATUS_MAST_RD_DATA_NO_ACK: /* 0x58 */
+		drv_data->action = MV64XXX_I2C_ACTION_RCV_DATA_STOP;
+		drv_data->state = MV64XXX_I2C_STATE_IDLE;
+		break;
+
+	case MV64XXX_I2C_STATUS_MAST_WR_ADDR_NO_ACK: /* 0x20 */
+	case MV64XXX_I2C_STATUS_MAST_WR_NO_ACK: /* 30 */
+	case MV64XXX_I2C_STATUS_MAST_RD_ADDR_NO_ACK: /* 48 */
+		/* Doesn't seem to be a device at other end */
+		drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
+		drv_data->state = MV64XXX_I2C_STATE_IDLE;
+		drv_data->rc = -ENODEV;
+		break;
+
+	default:
+		dev_err(&drv_data->adapter.dev,
+			"mv64xxx_i2c_fsm: Ctlr Error -- state: 0x%x, "
+			"status: 0x%x, addr: 0x%x, flags: 0x%x\n",
+			 drv_data->state, status, drv_data->msg->addr,
+			 drv_data->msg->flags);
+		drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
+		drv_data->state = MV64XXX_I2C_STATE_IDLE;
+		drv_data->rc = -EIO;
+	}
+}
+
+static void
+mv64xxx_i2c_do_action(struct mv64xxx_i2c_data *drv_data)
+{
+	switch(drv_data->action) {
+	case MV64XXX_I2C_ACTION_CONTINUE:
+		writel(drv_data->cntl_bits,
+			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+		break;
+
+	case MV64XXX_I2C_ACTION_SEND_START:
+		writel(drv_data->cntl_bits | MV64XXX_I2C_REG_CONTROL_START,
+			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+		break;
+
+	case MV64XXX_I2C_ACTION_SEND_ADDR_1:
+		writel(drv_data->addr1,
+			drv_data->reg_base + MV64XXX_I2C_REG_DATA);
+		writel(drv_data->cntl_bits,
+			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+		break;
+
+	case MV64XXX_I2C_ACTION_SEND_ADDR_2:
+		writel(drv_data->addr2,
+			drv_data->reg_base + MV64XXX_I2C_REG_DATA);
+		writel(drv_data->cntl_bits,
+			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+		break;
+
+	case MV64XXX_I2C_ACTION_SEND_DATA:
+		writel(drv_data->msg->buf[drv_data->byte_posn++],
+			drv_data->reg_base + MV64XXX_I2C_REG_DATA);
+		writel(drv_data->cntl_bits,
+			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+		break;
+
+	case MV64XXX_I2C_ACTION_RCV_DATA:
+		drv_data->msg->buf[drv_data->byte_posn++] =
+			readl(drv_data->reg_base + MV64XXX_I2C_REG_DATA);
+		writel(drv_data->cntl_bits,
+			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+		break;
+
+	case MV64XXX_I2C_ACTION_RCV_DATA_STOP:
+		drv_data->msg->buf[drv_data->byte_posn++] =
+			readl(drv_data->reg_base + MV64XXX_I2C_REG_DATA);
+		drv_data->cntl_bits &= ~MV64XXX_I2C_REG_CONTROL_INTEN;
+		writel(drv_data->cntl_bits | MV64XXX_I2C_REG_CONTROL_STOP,
+			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+		drv_data->block = 0;
+		wake_up_interruptible(&drv_data->waitq);
+		break;
+
+	case MV64XXX_I2C_ACTION_INVALID:
+	default:
+		dev_err(&drv_data->adapter.dev,
+			"mv64xxx_i2c_do_action: Invalid action: %d\n",
+			drv_data->action);
+		drv_data->rc = -EIO;
+		/* FALLTHRU */
+	case MV64XXX_I2C_ACTION_SEND_STOP:
+		drv_data->cntl_bits &= ~MV64XXX_I2C_REG_CONTROL_INTEN;
+		writel(drv_data->cntl_bits | MV64XXX_I2C_REG_CONTROL_STOP,
+			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+		drv_data->block = 0;
+		wake_up_interruptible(&drv_data->waitq);
+		break;
+	}
+}
+
+static int
+mv64xxx_i2c_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct mv64xxx_i2c_data	*drv_data = dev_id;
+	unsigned long	flags;
+	u32		status;
+	int		rc = IRQ_NONE;
+
+	spin_lock_irqsave(&drv_data->lock, flags);
+	while (readl(drv_data->reg_base + MV64XXX_I2C_REG_CONTROL) &
+						MV64XXX_I2C_REG_CONTROL_IFLG) {
+		status = readl(drv_data->reg_base + MV64XXX_I2C_REG_STATUS);
+		mv64xxx_i2c_fsm(drv_data, status);
+		mv64xxx_i2c_do_action(drv_data);
+		rc = IRQ_HANDLED;
+	}
+	spin_unlock_irqrestore(&drv_data->lock, flags);
+
+	return rc;
+}
+
+/*
+ *****************************************************************************
+ *
+ *	I2C Msg Execution Routines
+ *
+ *****************************************************************************
+ */
+static void
+mv64xxx_i2c_prepare_for_io(struct mv64xxx_i2c_data *drv_data,
+	struct i2c_msg *msg)
+{
+	u32	dir = 0;
+
+	drv_data->msg = msg;
+	drv_data->byte_posn = 0;
+	drv_data->bytes_left = msg->len;
+	drv_data->rc = 0;
+	drv_data->cntl_bits = MV64XXX_I2C_REG_CONTROL_ACK |
+		MV64XXX_I2C_REG_CONTROL_INTEN | MV64XXX_I2C_REG_CONTROL_TWSIEN;
+
+	if (msg->flags & I2C_M_RD)
+		dir = 1;
+
+	if (msg->flags & I2C_M_REV_DIR_ADDR)
+		dir ^= 1;
+
+	if (msg->flags & I2C_M_TEN) {
+		drv_data->addr1 = 0xf0 | (((u32)msg->addr & 0x300) >> 7) | dir;
+		drv_data->addr2 = (u32)msg->addr & 0xff;
+	} else {
+		drv_data->addr1 = ((u32)msg->addr & 0x7f) << 1 | dir;
+		drv_data->addr2 = 0;
+	}
+}
+
+static void
+mv64xxx_i2c_wait_for_completion(struct mv64xxx_i2c_data *drv_data)
+{
+	long		time_left;
+	unsigned long	flags;
+	char		abort = 0;
+
+	time_left = wait_event_interruptible_timeout(drv_data->waitq,
+		!drv_data->block, msecs_to_jiffies(drv_data->adapter.timeout));
+
+	spin_lock_irqsave(&drv_data->lock, flags);
+	if (!time_left) { /* Timed out */
+		drv_data->rc = -ETIMEDOUT;
+		abort = 1;
+	} else if (time_left < 0) { /* Interrupted/Error */
+		drv_data->rc = time_left; /* errno value */
+		abort = 1;
+	}
+
+	if (abort && drv_data->block) {
+		drv_data->state = MV64XXX_I2C_STATE_ABORTING;
+		spin_unlock_irqrestore(&drv_data->lock, flags);
+
+		time_left = wait_event_timeout(drv_data->waitq,
+			!drv_data->block,
+			msecs_to_jiffies(drv_data->adapter.timeout));
+
+		if (time_left <= 0) {
+			drv_data->state = MV64XXX_I2C_STATE_IDLE;
+			dev_err(&drv_data->adapter.dev,
+				"mv64xxx: I2C bus locked\n");
+		}
+	} else
+		spin_unlock_irqrestore(&drv_data->lock, flags);
+}
+
+static int
+mv64xxx_i2c_execute_msg(struct mv64xxx_i2c_data *drv_data, struct i2c_msg *msg)
+{
+	unsigned long	flags;
+
+	spin_lock_irqsave(&drv_data->lock, flags);
+	mv64xxx_i2c_prepare_for_io(drv_data, msg);
+
+	if (unlikely(msg->flags & I2C_M_NOSTART)) { /* Skip start/addr phases */
+		if (drv_data->msg->flags & I2C_M_RD) {
+			/* No action to do, wait for slave to send a byte */
+			drv_data->action = MV64XXX_I2C_ACTION_CONTINUE;
+			drv_data->state =
+				MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_DATA;
+		} else {
+			drv_data->action = MV64XXX_I2C_ACTION_SEND_DATA;
+			drv_data->state =
+				MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_ACK;
+			drv_data->bytes_left--;
+		}
+	} else {
+		drv_data->action = MV64XXX_I2C_ACTION_SEND_START;
+		drv_data->state = MV64XXX_I2C_STATE_WAITING_FOR_START_COND;
+	}
+
+	drv_data->block = 1;
+	mv64xxx_i2c_do_action(drv_data);
+	spin_unlock_irqrestore(&drv_data->lock, flags);
+
+	mv64xxx_i2c_wait_for_completion(drv_data);
+	return drv_data->rc;
+}
+
+/*
+ *****************************************************************************
+ *
+ *	I2C Core Support Routines (Interface to higher level I2C code)
+ *
+ *****************************************************************************
+ */
+static u32
+mv64xxx_i2c_functionality(struct i2c_adapter *adap)
+{
+	return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR | I2C_FUNC_SMBUS_EMUL;
+}
+
+static int
+mv64xxx_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
+{
+	struct mv64xxx_i2c_data *drv_data = i2c_get_adapdata(adap);
+	int	i, rc = 0;
+
+	for (i=0; i<num; i++)
+		if ((rc = mv64xxx_i2c_execute_msg(drv_data, &msgs[i])) != 0)
+			break;
+
+	return rc;
+}
+
+static struct i2c_algorithm mv64xxx_i2c_algo = {
+	.name = MV64XXX_I2C_CTLR_NAME " algorithm",
+	.id = I2C_ALGO_MV64XXX,
+	.master_xfer = mv64xxx_i2c_xfer,
+	.functionality = mv64xxx_i2c_functionality,
+};
+
+/*
+ *****************************************************************************
+ *
+ *	Driver Interface & Early Init Routines
+ *
+ *****************************************************************************
+ */
+static void __devinit
+mv64xxx_i2c_hw_init(struct mv64xxx_i2c_data *drv_data)
+{
+	writel(0, drv_data->reg_base + MV64XXX_I2C_REG_SOFT_RESET);
+	writel((((drv_data->freq_m & 0xf) << 3) | (drv_data->freq_n & 0x7)),
+		drv_data->reg_base + MV64XXX_I2C_REG_BAUD);
+	writel(0, drv_data->reg_base + MV64XXX_I2C_REG_SLAVE_ADDR);
+	writel(0, drv_data->reg_base + MV64XXX_I2C_REG_EXT_SLAVE_ADDR);
+	writel(MV64XXX_I2C_REG_CONTROL_TWSIEN | MV64XXX_I2C_REG_CONTROL_STOP,
+		drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+	drv_data->state = MV64XXX_I2C_STATE_IDLE;
+}
+
+static int __devinit
+mv64xxx_i2c_map_regs(struct platform_device *pd,
+	struct mv64xxx_i2c_data *drv_data)
+{
+	struct resource	*r;
+
+	if ((r = platform_get_resource(pd, IORESOURCE_MEM, 0)) &&
+		request_mem_region(r->start, MV64XXX_I2C_REG_BLOCK_SIZE,
+			drv_data->adapter.name)) {
+
+		drv_data->reg_base = ioremap(r->start,
+			MV64XXX_I2C_REG_BLOCK_SIZE);
+		drv_data->reg_base_p = r->start;
+	} else
+		return -ENOMEM;
+
+	return 0;
+}
+
+static void __devexit
+mv64xxx_i2c_unmap_regs(struct mv64xxx_i2c_data *drv_data)
+{
+	if (drv_data->reg_base) {
+		iounmap(drv_data->reg_base);
+		release_mem_region(drv_data->reg_base_p,
+			MV64XXX_I2C_REG_BLOCK_SIZE);
+	}
+
+	drv_data->reg_base = NULL;
+	drv_data->reg_base_p = 0;
+}
+
+static int __devinit
+mv64xxx_i2c_probe(struct device *dev)
+{
+	struct platform_device		*pd = to_platform_device(dev);
+	struct mv64xxx_i2c_data		*drv_data;
+	struct mv64xxx_i2c_pdata	*pdata = dev->platform_data;
+	int	rc;
+
+	if ((pd->id != 0) || !pdata)
+		return -ENODEV;
+
+	drv_data = kmalloc(sizeof(struct mv64xxx_i2c_data), GFP_KERNEL);
+
+	if (!drv_data)
+		return -ENOMEM;
+
+	memset(drv_data, 0, sizeof(struct mv64xxx_i2c_data));
+
+	if (mv64xxx_i2c_map_regs(pd, drv_data)) {
+		rc = -ENODEV;
+		goto exit_kfree;
+	}
+
+	strncpy(drv_data->adapter.name, MV64XXX_I2C_CTLR_NAME " adapter",
+		I2C_NAME_SIZE);
+
+	init_waitqueue_head(&drv_data->waitq);
+	spin_lock_init(&drv_data->lock);
+
+	drv_data->freq_m = pdata->freq_m;
+	drv_data->freq_n = pdata->freq_n;
+	drv_data->irq = platform_get_irq(pd, 0);
+	drv_data->adapter.id = I2C_ALGO_MV64XXX | I2C_HW_MV64XXX;
+	drv_data->adapter.algo = &mv64xxx_i2c_algo;
+	drv_data->adapter.timeout = pdata->timeout;
+	drv_data->adapter.retries = pdata->retries;
+	dev_set_drvdata(dev, drv_data);
+	i2c_set_adapdata(&drv_data->adapter, drv_data);
+
+	if (request_irq(drv_data->irq, mv64xxx_i2c_intr, 0,
+		MV64XXX_I2C_CTLR_NAME, drv_data)) {
+
+		dev_err(dev, "mv64xxx: Can't register intr handler "
+			"irq: %d\n", drv_data->irq);
+		rc = -EINVAL;
+		goto exit_unmap_regs;
+	} else if ((rc = i2c_add_adapter(&drv_data->adapter)) != 0) {
+		dev_err(dev, "mv64xxx: Can't add i2c adapter, rc: %d\n", -rc);
+		goto exit_free_irq;
+	}
+
+	mv64xxx_i2c_hw_init(drv_data);
+
+	return 0;
+
+	exit_free_irq:
+		free_irq(drv_data->irq, drv_data);
+	exit_unmap_regs:
+		mv64xxx_i2c_unmap_regs(drv_data);
+	exit_kfree:
+		kfree(drv_data);
+	return rc;
+}
+
+static int __devexit
+mv64xxx_i2c_remove(struct device *dev)
+{
+	struct mv64xxx_i2c_data		*drv_data = dev_get_drvdata(dev);
+	int	rc;
+
+	rc = i2c_del_adapter(&drv_data->adapter);
+	free_irq(drv_data->irq, drv_data);
+	mv64xxx_i2c_unmap_regs(drv_data);
+	kfree(drv_data);
+
+	return rc;
+}
+
+static struct device_driver mv64xxx_i2c_driver = {
+	.name	= MV64XXX_I2C_CTLR_NAME,
+	.bus	= &platform_bus_type,
+	.probe	= mv64xxx_i2c_probe,
+	.remove	= mv64xxx_i2c_remove,
+};
+
+static int __init
+mv64xxx_i2c_init(void)
+{
+	return driver_register(&mv64xxx_i2c_driver);
+}
+
+static void __exit
+mv64xxx_i2c_exit(void)
+{
+	driver_unregister(&mv64xxx_i2c_driver);
+}
+
+module_init(mv64xxx_i2c_init);
+module_exit(mv64xxx_i2c_exit);
+
+MODULE_AUTHOR("Mark A. Greer <mgreer@mvista.com>");
+MODULE_DESCRIPTION("Marvell mv64xxx host bridge i2c ctlr driver");
+MODULE_LICENSE("GPL");
diff -Nru a/include/linux/i2c-id.h b/include/linux/i2c-id.h
--- a/include/linux/i2c-id.h	2005-02-09 14:32:24 -07:00
+++ b/include/linux/i2c-id.h	2005-02-09 14:32:24 -07:00
@@ -311,4 +311,7 @@
 /* --- MCP107 adapter */
 #define I2C_HW_MPC107 0x00
 
+/* --- Marvell mv64xxx i2c adapter */
+#define I2C_HW_MV64XXX 0x00
+
 #endif /* LINUX_I2C_ID_H */
diff -Nru a/include/linux/mv643xx.h b/include/linux/mv643xx.h
--- a/include/linux/mv643xx.h	2005-02-09 14:32:24 -07:00
+++ b/include/linux/mv643xx.h	2005-02-09 14:32:24 -07:00
@@ -977,12 +977,9 @@
 /* I2C Registers                        */
 /****************************************/
 
-#define MV64340_I2C_SLAVE_ADDR                                      0xc000
-#define MV64340_I2C_EXTENDED_SLAVE_ADDR                             0xc010
-#define MV64340_I2C_DATA                                            0xc004
-#define MV64340_I2C_CONTROL                                         0xc008
-#define MV64340_I2C_STATUS_BAUDE_RATE                               0xc00C
-#define MV64340_I2C_SOFT_RESET                                      0xc01c
+#define MV64XXX_I2C_CTLR_NAME					"mv64xxx i2c"
+#define MV64XXX_I2C_OFFSET                                          0xc000
+#define MV64XXX_I2C_REG_BLOCK_SIZE                                  0x0020
 
 /****************************************/
 /* GPP Interface Registers              */
@@ -1083,6 +1080,14 @@
 	u8	brg_can_tune;
 	u8	brg_clk_src;
 	u32	brg_clk_freq;
+};
+
+/* i2c Platform Device, Driver Data */
+struct mv64xxx_i2c_pdata {
+	u32	freq_m;
+	u32	freq_n;
+	u32	timeout;	/* In milliseconds */
+	u32	retries;
 };
 
 #endif /* __ASM_MV64340_H */

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

* Re: [PATCH][I2C] Marvell mv64xxx i2c driver
  2005-02-09 21:33       ` Mark A. Greer
@ 2005-02-17 22:25         ` Greg KH
  0 siblings, 0 replies; 23+ messages in thread
From: Greg KH @ 2005-02-17 22:25 UTC (permalink / raw)
  To: Mark A. Greer; +Cc: Bartlomiej Zolnierkiewicz, LM Sensors, lkml

On Wed, Feb 09, 2005 at 02:33:59PM -0700, Mark A. Greer wrote:
> 
> I can't find any definitive policy on this.  I kind of like the explicit 
> return, I don't know why.  I've had others make the same comment, 
> though, so I'll remove them since it obviously bothers people.
> 
> Attached is a replacement patch.
> 
> Thanks again, Bartlomiej.
> 
> Mark
> --
> Marvell makes a line of host bridge for PPC and MIPS systems.  On those 
> bridges is an i2c controller.  This patch adds the driver for that i2c 
> controller.
> 
> Please apply.
> 
> Depends on patch submitted by Jean Delvare: 
> http://archives.andrew.net.au/lm-sensors/msg29405.html
> 
> Signed-off-by: Mark A. Greer <mgreer@mvista.com>

Applied, thanks.

greg k-h


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

* Re: [PATCH][I2C] Marvell mv64xxx i2c driver
  2005-02-04  9:45           ` Jean Delvare
@ 2005-02-06 14:36             ` Jean Delvare
  0 siblings, 0 replies; 23+ messages in thread
From: Jean Delvare @ 2005-02-06 14:36 UTC (permalink / raw)
  To: Alexey Dobriyan, Mark A. Greer; +Cc: Greg KH, LM Sensors, LKML

Hi all,

Quoting myself:
> I am as surprised as you are to see this here. I2C_ALGO_AU1550 should
> really be made a different value. There is also a problem with
> I2C_ALGO_PCA and I2C_ALGO_SIBYTE having the same value, which was
> already reported to Greg some days ago if memory serves. I think I
> will send a patch against 2.6.10-rc3 to Linus this evening, which
> fixes the broken algo IDs. That way Mark can keep the algorithm ID he
> is using right now, and each algorithm will get its own, unique ID, as
> should be.

I've changed my mind since. There isn't anything critical here and
pushing random patches to Linus right before he releases 2.6.11 wouldn't
have been particularly subtle. So I instead sent a patch to Greg, as can
be seen here:
http://archives.andrew.net.au/lm-sensors/msg29405.html

So Mark, providing Greg accepts this patch, you can assume that
I2C_ALGO_MV64XXX is already properly defined in Greg's tree (where your
own patch will end up) so you don't need to add it yourself. You will
still have to define I2C_HW_MV64XXX though.

Thanks,
-- 
Jean Delvare

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

* Re: [PATCH][I2C] Marvell mv64xxx i2c driver
  2005-02-04  0:04         ` Mark A. Greer
@ 2005-02-04  9:45           ` Jean Delvare
  2005-02-06 14:36             ` Jean Delvare
  0 siblings, 1 reply; 23+ messages in thread
From: Jean Delvare @ 2005-02-04  9:45 UTC (permalink / raw)
  To: mgreer, adobriyan; +Cc: Greg KH, LM Sensors, LKML


Hi Mark, Alexey,

> > >+					/* 0x170000 - USB		*/
> > >+					/* 0x180000 - Virtual buses	*/
> > >+#define I2C_ALGO_MV64XXX 0x190000       /* Marvell mv64xxx i2c ctlr	*/
> > 
> > While I searched for typos and you're fixing them, au1550 owned 0x170000.
> > 2.6.11-rc3 says:
> > 
> > 	#define I2C_ALGO_AU1550 0x170000 /* Au1550 PSC algorithm */
> > 
> > So, I'd remove first two comments.
>
> I added the comments b/c of this email from Jean Delvare,
> http://www.uwsg.iu.edu/hypermail/linux/kernel/0501.3/0977.html.  The
> relevant part being:
>
> "0x170000 is reserved within the legacy i2c project for an USB algorithm,
> and 0x180000 for virtual busses. Could you please use 0x190000 instead,
> so as to avoid future collisions?"
>
> It looks like I2C_ALGO_AU1550 was just added so my guess is Jean is
> correct and I2C_ALGO_AU1550 should be made 0x1a0000 (or I move mine back
> one).  Would someone confirm that 0x170000 is used by legacy i2c stuffs?
> I don't really know where to look.  If so, I can easily make a patch
> moving it back.

I am as surprised as you are to see this here. I2C_ALGO_AU1550 should
really be made a different value. There is also a problem with
I2C_ALGO_PCA and I2C_ALGO_SIBYTE having the same value, which was
already reported to Greg some days ago if memory serves. I think I will
send a patch against 2.6.10-rc3 to Linus this evening, which fixes the
broken algo IDs. That way Mark can keep the algorithm ID he is using
right now, and each algorithm will get its own, unique ID, as should be.

You can see the i2c-id.h list from the legacy i2c project here:
  http://www2.lm-sensors.nu/~lm78/cvs/i2c/kernel/i2c-id.h
I am indeed trying to keep it in sync with the one in Linux 2.6 (and with
the one in Linux 2.4 at times), in the hope it'll avoid confusion and
increase portability.

Thanks,
--
Jean Delvare

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

* Re: [PATCH][I2C] Marvell mv64xxx i2c driver
  2005-02-03 19:12     ` Mark A. Greer
@ 2005-02-04  0:38       ` Alexey Dobriyan
  2005-02-04  0:04         ` Mark A. Greer
  0 siblings, 1 reply; 23+ messages in thread
From: Alexey Dobriyan @ 2005-02-04  0:38 UTC (permalink / raw)
  To: Mark A. Greer; +Cc: Greg KH, phil, sensors, linux-kernel

On Thursday 03 February 2005 21:12, Mark A. Greer wrote:

> > >+		mv64xxx_i2c_fsm(drv_data, status);
> >
> >It can set drv_data->rc to -ENODEV or -EIO. In both cases ->action goes to
> >MV64XXX_I2C_ACTION_SEND_STOP and mv64xxx_i2c_do_action() will writel()
> >something. Is it correct to _not_ check ->rc here?
> 
> I think so.  It still needs to go into do_action even when rc != 0 (in 
> which case it'll do a STOP condition).

Ok. Thanks for the explanation. Agree, ->rc should be left as is.

> This patch is a replacement patch that should address your concerns 
> except maybe the mv64xxx_i2c_data.rc one.

> --- a/include/linux/i2c-id.h
> +++ b/include/linux/i2c-id.h

> +					/* 0x170000 - USB		*/
> +					/* 0x180000 - Virtual buses	*/
> +#define I2C_ALGO_MV64XXX 0x190000       /* Marvell mv64xxx i2c ctlr	*/

While I searched for typos and you're fixing them, au1550 owned 0x170000.
2.6.11-rc3 says:

	#define I2C_ALGO_AU1550 0x170000 /* Au1550 PSC algorithm */

So, I'd remove first two comments.

Oh, and the last note: current sparse and gcc 4 don't produce any warnings.

	Alexey

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

* Re: [PATCH][I2C] Marvell mv64xxx i2c driver
  2005-02-04  0:38       ` Alexey Dobriyan
@ 2005-02-04  0:04         ` Mark A. Greer
  2005-02-04  9:45           ` Jean Delvare
  0 siblings, 1 reply; 23+ messages in thread
From: Mark A. Greer @ 2005-02-04  0:04 UTC (permalink / raw)
  To: Alexey Dobriyan; +Cc: Greg KH, phil, sensors, linux-kernel, khali

Alexey Dobriyan wrote:

>On Thursday 03 February 2005 21:12, Mark A. Greer wrote:
>
>  
>
>>>>+		mv64xxx_i2c_fsm(drv_data, status);
>>>>        
>>>>
>>>It can set drv_data->rc to -ENODEV or -EIO. In both cases ->action goes to
>>>MV64XXX_I2C_ACTION_SEND_STOP and mv64xxx_i2c_do_action() will writel()
>>>something. Is it correct to _not_ check ->rc here?
>>>      
>>>
>>I think so.  It still needs to go into do_action even when rc != 0 (in 
>>which case it'll do a STOP condition).
>>    
>>
>
>Ok. Thanks for the explanation. Agree, ->rc should be left as is.
>

Okay.

>  
>
>>--- a/include/linux/i2c-id.h
>>+++ b/include/linux/i2c-id.h
>>    
>>
>
>  
>
>>+					/* 0x170000 - USB		*/
>>+					/* 0x180000 - Virtual buses	*/
>>+#define I2C_ALGO_MV64XXX 0x190000       /* Marvell mv64xxx i2c ctlr	*/
>>    
>>
>
>While I searched for typos and you're fixing them, au1550 owned 0x170000.
>2.6.11-rc3 says:
>
>	#define I2C_ALGO_AU1550 0x170000 /* Au1550 PSC algorithm */
>
>So, I'd remove first two comments.
>

I added the comments b/c of this email from Jean Delvare, 
http://www.uwsg.iu.edu/hypermail/linux/kernel/0501.3/0977.html.  The 
relevant part being:

"0x170000 is reserved within the legacy i2c project for an USB algorithm,
and 0x180000 for virtual busses. Could you please use 0x190000 instead,
so as to avoid future collisions?"

It looks like I2C_ALGO_AU1550 was just added so my guess is Jean is 
correct and I2C_ALGO_AU1550 should be made 0x1a0000 (or I move mine back 
one).  Would someone confirm that 0x170000 is used by legacy i2c stuffs? 
I don't really know where to look.  If so, I can easily make a patch 
moving it back.

>Oh, and the last note: current sparse and gcc 4 don't produce any warnings.
>  
>

Cool!

Mark


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

* Re: [PATCH][I2C] Marvell mv64xxx i2c driver
       [not found]   ` <200502031556.59319.adobriyan@mail.ru>
@ 2005-02-03 19:12     ` Mark A. Greer
  2005-02-04  0:38       ` Alexey Dobriyan
  0 siblings, 1 reply; 23+ messages in thread
From: Mark A. Greer @ 2005-02-03 19:12 UTC (permalink / raw)
  To: Alexey Dobriyan; +Cc: Greg KH, phil, sensors, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 3216 bytes --]

Hi, Alexey.
--

Alexey Dobriyan wrote:

>On Wednesday 02 February 2005 19:26, Mark A. Greer wrote:
>
>  
>
>>How's this (a complete replacement for previous patch)?
>>    
>>
>
>  
>
>>--- a/drivers/i2c/busses/Kconfig
>>+++ b/drivers/i2c/busses/Kconfig
>>    
>>
>
>  
>
>>+	  If you say yes to this option, support will be included for the
>>+	  built-in I2C interface on the Marvell 64xxx line of host bridges
>>    
>>
>
>Dot at the end. Should have noticed it earlier...
>

Gahh, I noticed that too a while back.  I thought I'd fixed it...  Done.

>  
>
>>+	if (drv_data->state == MV64XXX_I2C_STATE_IDLE) {
>>+		drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
>>+		drv_data->state = MV64XXX_I2C_STATE_IDLE;
>>    
>>
>
>drv_data->state is already MV64XXX_I2C_STATE_IDLE. Gcc will probably optimize
>this line away, but...
>

Done away with.

>  
>
>>+		}
>>+		else {
>>    
>>
>
>It should be "} else {" according to CodingStyle.
>

Done.

>  
>
>>+static int
>>+mv64xxx_i2c_intr(int irq, void *dev_id, struct pt_regs *regs)
>>    
>>
>
>  
>
>>+	while (readl(drv_data->reg_base + MV64XXX_I2C_REG_CONTROL) &
>>+						MV64XXX_I2C_REG_CONTROL_IFLG) {
>>+		status = readl(drv_data->reg_base + MV64XXX_I2C_REG_STATUS);
>>+		mv64xxx_i2c_fsm(drv_data, status);
>>    
>>
>
>It can set drv_data->rc to -ENODEV or -EIO. In both cases ->action goes to
>MV64XXX_I2C_ACTION_SEND_STOP and mv64xxx_i2c_do_action() will writel()
>something. Is it correct to _not_ check ->rc here?
>

I think so.  It still needs to go into do_action even when rc != 0 (in 
which case it'll do a STOP condition).

> If it isn't then would it be
>better to make all functions that set it return -E___ instead and drop
>struct mv64xxx_i2c_data.rc altogether?
>

I may not be understanding what you mean but I think I need something 
like mv64xxx_i2c_data.rc or a plain old global to hang onto the return 
code.  I need that so when the wait_event_interruptible_timeout() 
returns, that thread can find out what happened while it was blocked.  
This is what I mean:

- calling thread enters i2c_xfer
- eventually the initial i2c action is executed and the thread blocks in 
wait_event_interruptible_timeout()
- other processes run
- several interrupts happen, last one causing an error which is stored 
in mv64xxx_i2c_data.rc
- do_action issues a stop on the i2c bus
- thread is unblocked and now has to dig out the rc from mv64xxx_i2c_data.rc

Or am I not understanding what you mean?

>+
>+		if (!time_left <= 0) {
>  
>
>
>Confusing. You meant "if (time_left)" or "if (time_left > 0)"?
>

No, I'm blind.  I meant "if (time_left <=0)".   Should be fixed now.  
Good catch.

>  
>
>>+static struct i2c_algorithm mv64xxx_i2c_algo = {
>>+	.name = MV64XXX_I2C_CTLR_NAME "algorithm",
>>    
>>
>
>MV64XXX_I2C_CTLR_NAME doesn't end with space. " algorithm" here.
>

Space added.

>  
>
>>+		dev_err(dev, "mv64xxx: Can't register intr handler "
>>+			"irq: %d\\n", drv_data->irq);
>>    
>>
>
>You snipped s# \\n # \n # suggestion in my previous email. ;-)
>

Ah, got it this time.  :)

This patch is a replacement patch that should address your concerns 
except maybe the mv64xxx_i2c_data.rc one.

Signed-off-by: Mark A. Greer <mgreer@mvista.com>
--

[-- Attachment #2: i2c_6.patch --]
[-- Type: text/plain, Size: 20901 bytes --]

diff -Nru a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
--- a/drivers/i2c/busses/Kconfig	2005-02-03 12:10:35 -07:00
+++ b/drivers/i2c/busses/Kconfig	2005-02-03 12:10:35 -07:00
@@ -476,4 +476,14 @@
 	  This driver can also be built as a module.  If so, the module
 	  will be called i2c-pca-isa.
 
+config I2C_MV64XXX
+	tristate "Marvell mv64xxx I2C Controller"
+	depends on I2C && MV64X60 && EXPERIMENTAL
+	help
+	  If you say yes to this option, support will be included for the
+	  built-in I2C interface on the Marvell 64xxx line of host bridges.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called i2c-mv64xxx.
+
 endmenu
diff -Nru a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
--- a/drivers/i2c/busses/Makefile	2005-02-03 12:10:35 -07:00
+++ b/drivers/i2c/busses/Makefile	2005-02-03 12:10:35 -07:00
@@ -20,6 +20,7 @@
 obj-$(CONFIG_I2C_IXP4XX)	+= i2c-ixp4xx.o
 obj-$(CONFIG_I2C_KEYWEST)	+= i2c-keywest.o
 obj-$(CONFIG_I2C_MPC)		+= i2c-mpc.o
+obj-$(CONFIG_I2C_MV64XXX)	+= i2c-mv64xxx.o
 obj-$(CONFIG_I2C_NFORCE2)	+= i2c-nforce2.o
 obj-$(CONFIG_I2C_PARPORT)	+= i2c-parport.o
 obj-$(CONFIG_I2C_PARPORT_LIGHT)	+= i2c-parport-light.o
diff -Nru a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/drivers/i2c/busses/i2c-mv64xxx.c	2005-02-03 12:10:35 -07:00
@@ -0,0 +1,606 @@
+/*
+ * drivers/i2c/busses/i2c-mv64xxx.c
+ * 
+ * Driver for the i2c controller on the Marvell line of host bridges for MIPS
+ * and PPC (e.g, gt642[46]0, mv643[46]0, mv644[46]0).
+ *
+ * Author: Mark A. Greer <mgreer@mvista.com>
+ *
+ * 2005 (c) MontaVista, Software, Inc.  This file is licensed under
+ * the terms of the GNU General Public License version 2.  This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/mv643xx.h>
+#include <asm/io.h>
+
+/* Register defines */
+#define	MV64XXX_I2C_REG_SLAVE_ADDR			0x00
+#define	MV64XXX_I2C_REG_DATA				0x04
+#define	MV64XXX_I2C_REG_CONTROL				0x08
+#define	MV64XXX_I2C_REG_STATUS				0x0c
+#define	MV64XXX_I2C_REG_BAUD				0x0c
+#define	MV64XXX_I2C_REG_EXT_SLAVE_ADDR			0x10
+#define	MV64XXX_I2C_REG_SOFT_RESET			0x1c
+
+#define	MV64XXX_I2C_REG_CONTROL_ACK			0x00000004
+#define	MV64XXX_I2C_REG_CONTROL_IFLG			0x00000008
+#define	MV64XXX_I2C_REG_CONTROL_STOP			0x00000010
+#define	MV64XXX_I2C_REG_CONTROL_START			0x00000020
+#define	MV64XXX_I2C_REG_CONTROL_TWSIEN			0x00000040
+#define	MV64XXX_I2C_REG_CONTROL_INTEN			0x00000080
+
+/* Ctlr status values */
+#define	MV64XXX_I2C_STATUS_BUS_ERR			0x00
+#define	MV64XXX_I2C_STATUS_MAST_START			0x08
+#define	MV64XXX_I2C_STATUS_MAST_REPEAT_START		0x10
+#define	MV64XXX_I2C_STATUS_MAST_WR_ADDR_ACK		0x18
+#define	MV64XXX_I2C_STATUS_MAST_WR_ADDR_NO_ACK		0x20
+#define	MV64XXX_I2C_STATUS_MAST_WR_ACK			0x28
+#define	MV64XXX_I2C_STATUS_MAST_WR_NO_ACK		0x30
+#define	MV64XXX_I2C_STATUS_MAST_LOST_ARB		0x38
+#define	MV64XXX_I2C_STATUS_MAST_RD_ADDR_ACK		0x40
+#define	MV64XXX_I2C_STATUS_MAST_RD_ADDR_NO_ACK		0x48
+#define	MV64XXX_I2C_STATUS_MAST_RD_DATA_ACK		0x50
+#define	MV64XXX_I2C_STATUS_MAST_RD_DATA_NO_ACK		0x58
+#define	MV64XXX_I2C_STATUS_MAST_WR_ADDR_2_ACK		0xd0
+#define	MV64XXX_I2C_STATUS_MAST_WR_ADDR_2_NO_ACK	0xd8
+#define	MV64XXX_I2C_STATUS_MAST_RD_ADDR_2_ACK		0xe0
+#define	MV64XXX_I2C_STATUS_MAST_RD_ADDR_2_NO_ACK	0xe8
+#define	MV64XXX_I2C_STATUS_NO_STATUS			0xf8
+
+/* Driver states */
+enum {
+	MV64XXX_I2C_STATE_INVALID,
+	MV64XXX_I2C_STATE_IDLE,
+	MV64XXX_I2C_STATE_WAITING_FOR_START_COND,
+	MV64XXX_I2C_STATE_WAITING_FOR_ADDR_1_ACK,
+	MV64XXX_I2C_STATE_WAITING_FOR_ADDR_2_ACK,
+	MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_ACK,
+	MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_DATA,
+	MV64XXX_I2C_STATE_ABORTING,
+};
+
+/* Driver actions */
+enum {
+	MV64XXX_I2C_ACTION_INVALID,
+	MV64XXX_I2C_ACTION_CONTINUE,
+	MV64XXX_I2C_ACTION_SEND_START,
+	MV64XXX_I2C_ACTION_SEND_ADDR_1,
+	MV64XXX_I2C_ACTION_SEND_ADDR_2,
+	MV64XXX_I2C_ACTION_SEND_DATA,
+	MV64XXX_I2C_ACTION_RCV_DATA,
+	MV64XXX_I2C_ACTION_RCV_DATA_STOP,
+	MV64XXX_I2C_ACTION_SEND_STOP,
+};
+
+struct mv64xxx_i2c_data {
+	int			irq;
+	u32			state;
+	u32			action;
+	u32			cntl_bits;
+	void __iomem		*reg_base;
+	u32			reg_base_p;
+	u32			addr1;
+	u32			addr2;
+	u32			bytes_left;
+	u32			byte_posn;
+	u32			block;
+	int			rc;
+	u32			freq_m;
+	u32			freq_n;
+	wait_queue_head_t	waitq;
+	spinlock_t		lock;
+	struct i2c_msg		*msg;
+	struct i2c_adapter	adapter;
+};
+
+/*
+ *****************************************************************************
+ *
+ *	Finite State Machine & Interrupt Routines
+ *
+ *****************************************************************************
+ */
+static void
+mv64xxx_i2c_fsm(struct mv64xxx_i2c_data *drv_data, u32 status)
+{
+	/*
+	 * If state is idle, then this is likely the remnants of an old
+	 * operation that driver has given up on or the user has killed.
+	 * If so, issue the stop condition and go to idle.
+	 */
+	if (drv_data->state == MV64XXX_I2C_STATE_IDLE) {
+		drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
+		return;
+	}
+
+	if (drv_data->state == MV64XXX_I2C_STATE_ABORTING) {
+		drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
+		drv_data->state = MV64XXX_I2C_STATE_IDLE;
+		return;
+	}
+
+	/* The status from the ctlr [mostly] tells us what to do next */
+	switch (status) {
+	/* Start condition interrupt */
+	case MV64XXX_I2C_STATUS_MAST_START: /* 0x08 */
+	case MV64XXX_I2C_STATUS_MAST_REPEAT_START: /* 0x10 */
+		drv_data->action = MV64XXX_I2C_ACTION_SEND_ADDR_1;
+		drv_data->state = MV64XXX_I2C_STATE_WAITING_FOR_ADDR_1_ACK;
+		break;
+
+	/* Performing a write */
+	case MV64XXX_I2C_STATUS_MAST_WR_ADDR_ACK: /* 0x18 */
+		if (drv_data->msg->flags & I2C_M_TEN) {
+			drv_data->action = MV64XXX_I2C_ACTION_SEND_ADDR_2;
+			drv_data->state =
+				MV64XXX_I2C_STATE_WAITING_FOR_ADDR_2_ACK;
+			break;
+		}
+		/* FALLTHRU */
+	case MV64XXX_I2C_STATUS_MAST_WR_ADDR_2_ACK: /* 0xd0 */
+	case MV64XXX_I2C_STATUS_MAST_WR_ACK: /* 0x28 */
+		if (drv_data->bytes_left > 0) {
+			drv_data->action = MV64XXX_I2C_ACTION_SEND_DATA;
+			drv_data->state =
+				MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_ACK;
+			drv_data->bytes_left--;
+		} else {
+			drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
+			drv_data->state = MV64XXX_I2C_STATE_IDLE;
+		}
+		break;
+
+	/* Performing a read */
+	case MV64XXX_I2C_STATUS_MAST_RD_ADDR_ACK: /* 40 */
+		if (drv_data->msg->flags & I2C_M_TEN) {
+			drv_data->action = MV64XXX_I2C_ACTION_SEND_ADDR_2;
+			drv_data->state =
+				MV64XXX_I2C_STATE_WAITING_FOR_ADDR_2_ACK;
+			break;
+		}
+		/* FALLTHRU */
+	case MV64XXX_I2C_STATUS_MAST_RD_ADDR_2_ACK: /* 0xe0 */
+		if (drv_data->bytes_left == 0) {
+			drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
+			drv_data->state = MV64XXX_I2C_STATE_IDLE;
+			break;
+		}
+		/* FALLTHRU */
+	case MV64XXX_I2C_STATUS_MAST_RD_DATA_ACK: /* 0x50 */
+		if (status != MV64XXX_I2C_STATUS_MAST_RD_DATA_ACK)
+			drv_data->action = MV64XXX_I2C_ACTION_CONTINUE;
+		else {
+			drv_data->action = MV64XXX_I2C_ACTION_RCV_DATA;
+			drv_data->bytes_left--;
+		}
+		drv_data->state = MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_DATA;
+
+		if (drv_data->bytes_left == 1)
+			drv_data->cntl_bits &= ~MV64XXX_I2C_REG_CONTROL_ACK;
+		break;
+
+	case MV64XXX_I2C_STATUS_MAST_RD_DATA_NO_ACK: /* 0x58 */
+		drv_data->action = MV64XXX_I2C_ACTION_RCV_DATA_STOP;
+		drv_data->state = MV64XXX_I2C_STATE_IDLE;
+		break;
+
+	case MV64XXX_I2C_STATUS_MAST_WR_ADDR_NO_ACK: /* 0x20 */
+	case MV64XXX_I2C_STATUS_MAST_WR_NO_ACK: /* 30 */
+	case MV64XXX_I2C_STATUS_MAST_RD_ADDR_NO_ACK: /* 48 */
+		/* Doesn't seem to be a device at other end */
+		drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
+		drv_data->state = MV64XXX_I2C_STATE_IDLE;
+		drv_data->rc = -ENODEV;
+		break;
+
+	default:
+		dev_err(&drv_data->adapter.dev,
+			"mv64xxx_i2c_fsm: Ctlr Error -- state: 0x%x, "
+			"status: 0x%x, addr: 0x%x, flags: 0x%x\n",
+			 drv_data->state, status, drv_data->msg->addr,
+			 drv_data->msg->flags);
+		drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
+		drv_data->state = MV64XXX_I2C_STATE_IDLE;
+		drv_data->rc = -EIO;
+	}
+
+	return;
+}
+
+static void
+mv64xxx_i2c_do_action(struct mv64xxx_i2c_data *drv_data)
+{
+	switch(drv_data->action) {
+	case MV64XXX_I2C_ACTION_CONTINUE:
+		writel(drv_data->cntl_bits,
+			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+		break;
+
+	case MV64XXX_I2C_ACTION_SEND_START:
+		writel(drv_data->cntl_bits | MV64XXX_I2C_REG_CONTROL_START,
+			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+		break;
+
+	case MV64XXX_I2C_ACTION_SEND_ADDR_1:
+		writel(drv_data->addr1,
+			drv_data->reg_base + MV64XXX_I2C_REG_DATA);
+		writel(drv_data->cntl_bits,
+			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+		break;
+
+	case MV64XXX_I2C_ACTION_SEND_ADDR_2:
+		writel(drv_data->addr2,
+			drv_data->reg_base + MV64XXX_I2C_REG_DATA);
+		writel(drv_data->cntl_bits,
+			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+		break;
+
+	case MV64XXX_I2C_ACTION_SEND_DATA:
+		writel(drv_data->msg->buf[drv_data->byte_posn++],
+			drv_data->reg_base + MV64XXX_I2C_REG_DATA);
+		writel(drv_data->cntl_bits,
+			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+		break;
+
+	case MV64XXX_I2C_ACTION_RCV_DATA:
+		drv_data->msg->buf[drv_data->byte_posn++] =
+			readl(drv_data->reg_base + MV64XXX_I2C_REG_DATA);
+		writel(drv_data->cntl_bits,
+			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+		break;
+
+	case MV64XXX_I2C_ACTION_RCV_DATA_STOP:
+		drv_data->msg->buf[drv_data->byte_posn++] =
+			readl(drv_data->reg_base + MV64XXX_I2C_REG_DATA);
+		drv_data->cntl_bits &= ~MV64XXX_I2C_REG_CONTROL_INTEN;
+		writel(drv_data->cntl_bits | MV64XXX_I2C_REG_CONTROL_STOP,
+			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+		drv_data->block = 0;
+		wake_up_interruptible(&drv_data->waitq);
+		break;
+
+	case MV64XXX_I2C_ACTION_INVALID:
+	default:
+		dev_err(&drv_data->adapter.dev,
+			"mv64xxx_i2c_do_action: Invalid action: %d\n",
+			drv_data->action);
+		drv_data->rc = -EIO;
+		/* FALLTHRU */
+	case MV64XXX_I2C_ACTION_SEND_STOP:
+		drv_data->cntl_bits &= ~MV64XXX_I2C_REG_CONTROL_INTEN;
+		writel(drv_data->cntl_bits | MV64XXX_I2C_REG_CONTROL_STOP,
+			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+		drv_data->block = 0;
+		wake_up_interruptible(&drv_data->waitq);
+		break;
+	}
+
+	return;
+}
+
+static int
+mv64xxx_i2c_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct mv64xxx_i2c_data	*drv_data = dev_id;
+	u32	status;
+	long	flags;
+	int	rc = IRQ_NONE;
+
+	spin_lock_irqsave(&drv_data->lock, flags);
+	while (readl(drv_data->reg_base + MV64XXX_I2C_REG_CONTROL) &
+						MV64XXX_I2C_REG_CONTROL_IFLG) {
+		status = readl(drv_data->reg_base + MV64XXX_I2C_REG_STATUS);
+		mv64xxx_i2c_fsm(drv_data, status);
+		mv64xxx_i2c_do_action(drv_data);
+		rc = IRQ_HANDLED;
+	}
+	spin_unlock_irqrestore(&drv_data->lock, flags);
+
+	return rc;
+}
+
+/*
+ *****************************************************************************
+ *
+ *	I2C Msg Execution Routines
+ *
+ *****************************************************************************
+ */
+static void
+mv64xxx_i2c_prepare_for_io(struct mv64xxx_i2c_data *drv_data,
+	struct i2c_msg *msg)
+{
+	u32	dir = 0;
+
+	drv_data->msg = msg;
+	drv_data->byte_posn = 0;
+	drv_data->bytes_left = msg->len;
+	drv_data->rc = 0;
+	drv_data->cntl_bits = MV64XXX_I2C_REG_CONTROL_ACK |
+		MV64XXX_I2C_REG_CONTROL_INTEN | MV64XXX_I2C_REG_CONTROL_TWSIEN;
+
+	if (msg->flags & I2C_M_RD)
+		dir = 1;
+
+	if (msg->flags & I2C_M_REV_DIR_ADDR)
+		dir ^= 1;
+
+	if (msg->flags & I2C_M_TEN) {
+		drv_data->addr1 = 0xf0 | (((u32)msg->addr & 0x300) >> 7) | dir;
+		drv_data->addr2 = (u32)msg->addr & 0xff;
+	} else {
+		drv_data->addr1 = ((u32)msg->addr & 0x7f) << 1 | dir;
+		drv_data->addr2 = 0;
+	}
+
+	return;
+}
+
+static void
+mv64xxx_i2c_wait_for_completion(struct mv64xxx_i2c_data *drv_data)
+{
+	long	flags, time_left;
+	char	abort = 0;
+
+	time_left = wait_event_interruptible_timeout(drv_data->waitq,
+		!drv_data->block, msecs_to_jiffies(drv_data->adapter.timeout));
+
+	spin_lock_irqsave(&drv_data->lock, flags);
+	if (!time_left) { /* Timed out */
+		drv_data->rc = -ETIMEDOUT;
+		abort = 1;
+	} else if (time_left < 0) { /* Interrupted/Error */
+		drv_data->rc = time_left; /* errno value */
+		abort = 1;
+	}
+
+	if (abort && drv_data->block) {
+		drv_data->state = MV64XXX_I2C_STATE_ABORTING;
+		spin_unlock_irqrestore(&drv_data->lock, flags);
+
+		time_left = wait_event_timeout(drv_data->waitq,
+			!drv_data->block,
+			msecs_to_jiffies(drv_data->adapter.timeout));
+
+		if (time_left <= 0) {
+			drv_data->state = MV64XXX_I2C_STATE_IDLE;
+			dev_err(&drv_data->adapter.dev,
+				"mv64xxx: I2C bus locked\n");
+		}
+	} else
+		spin_unlock_irqrestore(&drv_data->lock, flags);
+
+	return;
+}
+
+static int
+mv64xxx_i2c_execute_msg(struct mv64xxx_i2c_data *drv_data, struct i2c_msg *msg)
+{
+	long	flags;
+
+	spin_lock_irqsave(&drv_data->lock, flags);
+	mv64xxx_i2c_prepare_for_io(drv_data, msg);
+
+	if (unlikely(msg->flags & I2C_M_NOSTART)) { /* Skip start/addr phases */
+		if (drv_data->msg->flags & I2C_M_RD) {
+			/* No action to do, wait for slave to send a byte */
+			drv_data->action = MV64XXX_I2C_ACTION_CONTINUE;
+			drv_data->state =
+				MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_DATA;
+		} else {
+			drv_data->action = MV64XXX_I2C_ACTION_SEND_DATA;
+			drv_data->state =
+				MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_ACK;
+			drv_data->bytes_left--;
+		}
+	} else {
+		drv_data->action = MV64XXX_I2C_ACTION_SEND_START;
+		drv_data->state = MV64XXX_I2C_STATE_WAITING_FOR_START_COND;
+	}
+
+	drv_data->block = 1;
+	mv64xxx_i2c_do_action(drv_data);
+	spin_unlock_irqrestore(&drv_data->lock, flags);
+
+	mv64xxx_i2c_wait_for_completion(drv_data);
+	return drv_data->rc;
+}
+
+/*
+ *****************************************************************************
+ *
+ *	I2C Core Support Routines (Interface to higher level I2C code)
+ *
+ *****************************************************************************
+ */
+static u32
+mv64xxx_i2c_functionality(struct i2c_adapter *adap)
+{
+	return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR | I2C_FUNC_SMBUS_EMUL;
+}
+
+static int
+mv64xxx_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
+{
+	struct mv64xxx_i2c_data *drv_data = i2c_get_adapdata(adap);
+	int	i, rc = 0;
+
+	for (i=0; i<num; i++)
+		if ((rc = mv64xxx_i2c_execute_msg(drv_data, &msgs[i])) != 0)
+			break;
+
+	return rc;
+}
+
+static struct i2c_algorithm mv64xxx_i2c_algo = {
+	.name = MV64XXX_I2C_CTLR_NAME " algorithm",
+	.id = I2C_ALGO_MV64XXX,
+	.master_xfer = mv64xxx_i2c_xfer,
+	.functionality = mv64xxx_i2c_functionality,
+};
+
+/*
+ *****************************************************************************
+ *
+ *	Driver Interface & Early Init Routines
+ *
+ *****************************************************************************
+ */
+static void __devinit
+mv64xxx_i2c_hw_init(struct mv64xxx_i2c_data *drv_data)
+{
+	writel(0, drv_data->reg_base + MV64XXX_I2C_REG_SOFT_RESET);
+	writel((((drv_data->freq_m & 0xf) << 3) | (drv_data->freq_n & 0x7)),
+		drv_data->reg_base + MV64XXX_I2C_REG_BAUD);
+	writel(0, drv_data->reg_base + MV64XXX_I2C_REG_SLAVE_ADDR);
+	writel(0, drv_data->reg_base + MV64XXX_I2C_REG_EXT_SLAVE_ADDR);
+	writel(MV64XXX_I2C_REG_CONTROL_TWSIEN | MV64XXX_I2C_REG_CONTROL_STOP,
+		drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+	drv_data->state = MV64XXX_I2C_STATE_IDLE;
+	return;
+}
+
+static int __devinit
+mv64xxx_i2c_map_regs(struct platform_device *pd,
+	struct mv64xxx_i2c_data *drv_data)
+{
+	struct resource	*r;
+
+	if ((r = platform_get_resource(pd, IORESOURCE_MEM, 0)) &&
+		request_mem_region(r->start, MV64XXX_I2C_REG_BLOCK_SIZE,
+			drv_data->adapter.name)) {
+
+		drv_data->reg_base = ioremap(r->start,
+			MV64XXX_I2C_REG_BLOCK_SIZE);
+		drv_data->reg_base_p = r->start;
+	} else
+		return -ENOMEM;
+
+	return 0;
+}
+
+static void __devexit
+mv64xxx_i2c_unmap_regs(struct mv64xxx_i2c_data *drv_data)
+{
+	if (drv_data->reg_base) {
+		iounmap(drv_data->reg_base);
+		release_mem_region(drv_data->reg_base_p,
+			MV64XXX_I2C_REG_BLOCK_SIZE);
+	}
+
+	drv_data->reg_base = NULL;
+	drv_data->reg_base_p = 0;
+	return;
+}
+
+static int __devinit
+mv64xxx_i2c_probe(struct device *dev)
+{
+	struct platform_device		*pd = to_platform_device(dev);
+	struct mv64xxx_i2c_data		*drv_data;
+	struct mv64xxx_i2c_pdata	*pdata = dev->platform_data;
+	int	rc;
+
+	if ((pd->id != 0) || !pdata)
+		return -ENODEV;
+
+	drv_data = kmalloc(sizeof(struct mv64xxx_i2c_data), GFP_KERNEL);
+
+	if (!drv_data)
+		return -ENOMEM;
+
+	memset(drv_data, 0, sizeof(struct mv64xxx_i2c_data));
+
+	if (mv64xxx_i2c_map_regs(pd, drv_data)) {
+		rc = -ENODEV;
+		goto exit_kfree;
+	}
+
+	strncpy(drv_data->adapter.name, MV64XXX_I2C_CTLR_NAME " adapter",
+		I2C_NAME_SIZE);
+
+	init_waitqueue_head(&drv_data->waitq);
+	spin_lock_init(&drv_data->lock);
+
+	drv_data->freq_m = pdata->freq_m;
+	drv_data->freq_n = pdata->freq_n;
+	drv_data->irq = platform_get_irq(pd, 0);
+	drv_data->adapter.id = I2C_ALGO_MV64XXX | I2C_HW_MV64XXX;
+	drv_data->adapter.algo = &mv64xxx_i2c_algo;
+	drv_data->adapter.timeout = pdata->timeout;
+	drv_data->adapter.retries = pdata->retries;
+	dev_set_drvdata(dev, drv_data);
+	i2c_set_adapdata(&drv_data->adapter, drv_data);
+
+	if (request_irq(drv_data->irq, mv64xxx_i2c_intr, 0,
+		MV64XXX_I2C_CTLR_NAME, drv_data)) {
+
+		dev_err(dev, "mv64xxx: Can't register intr handler "
+			"irq: %d\n", drv_data->irq);
+		rc = -EINVAL;
+		goto exit_unmap_regs;
+	} else if ((rc = i2c_add_adapter(&drv_data->adapter)) != 0) {
+		dev_err(dev, "mv64xxx: Can't add i2c adapter, rc: %d\n", -rc);
+		goto exit_free_irq;
+	}
+
+	mv64xxx_i2c_hw_init(drv_data);
+
+	return 0;
+
+	exit_free_irq:
+		free_irq(drv_data->irq, drv_data);
+	exit_unmap_regs:
+		mv64xxx_i2c_unmap_regs(drv_data);
+	exit_kfree:
+		kfree(drv_data);
+	return rc;
+}
+
+static int __devexit
+mv64xxx_i2c_remove(struct device *dev)
+{
+	struct mv64xxx_i2c_data		*drv_data = dev_get_drvdata(dev);
+	int	rc;
+
+	rc = i2c_del_adapter(&drv_data->adapter);
+	free_irq(drv_data->irq, drv_data);
+	mv64xxx_i2c_unmap_regs(drv_data);
+	kfree(drv_data);
+
+	return rc;
+}
+
+static struct device_driver mv64xxx_i2c_driver = {
+	.name	= MV64XXX_I2C_CTLR_NAME,
+	.bus	= &platform_bus_type,
+	.probe	= mv64xxx_i2c_probe,
+	.remove	= mv64xxx_i2c_remove,
+};
+
+static int __devinit
+mv64xxx_i2c_init(void)
+{
+	return driver_register(&mv64xxx_i2c_driver);
+}
+
+static void __devexit
+mv64xxx_i2c_exit(void)
+{
+	driver_unregister(&mv64xxx_i2c_driver);
+	return;
+}
+
+module_init(mv64xxx_i2c_init);
+module_exit(mv64xxx_i2c_exit);
+
+MODULE_AUTHOR("Mark A. Greer <mgreer@mvista.com>");
+MODULE_DESCRIPTION("Marvell mv64xxx host bridge i2c ctlr driver");
+MODULE_LICENSE("GPL");
diff -Nru a/include/linux/i2c-id.h b/include/linux/i2c-id.h
--- a/include/linux/i2c-id.h	2005-02-03 12:10:35 -07:00
+++ b/include/linux/i2c-id.h	2005-02-03 12:10:35 -07:00
@@ -200,6 +200,9 @@
 
 #define I2C_ALGO_SIBYTE 0x150000	/* Broadcom SiByte SOCs		*/
 #define I2C_ALGO_SGI	0x160000        /* SGI algorithm                */
+					/* 0x170000 - USB		*/
+					/* 0x180000 - Virtual buses	*/
+#define I2C_ALGO_MV64XXX 0x190000       /* Marvell mv64xxx i2c ctlr	*/
 
 #define I2C_ALGO_EXP	0x800000	/* experimental			*/
 
@@ -305,5 +308,8 @@
 
 /* --- MCP107 adapter */
 #define I2C_HW_MPC107 0x00
+
+/* --- Marvell mv64xxx i2c adapter */
+#define I2C_HW_MV64XXX 0x00
 
 #endif /* LINUX_I2C_ID_H */
diff -Nru a/include/linux/mv643xx.h b/include/linux/mv643xx.h
--- a/include/linux/mv643xx.h	2005-02-03 12:10:35 -07:00
+++ b/include/linux/mv643xx.h	2005-02-03 12:10:35 -07:00
@@ -977,12 +977,9 @@
 /* I2C Registers                        */
 /****************************************/
 
-#define MV64340_I2C_SLAVE_ADDR                                      0xc000
-#define MV64340_I2C_EXTENDED_SLAVE_ADDR                             0xc010
-#define MV64340_I2C_DATA                                            0xc004
-#define MV64340_I2C_CONTROL                                         0xc008
-#define MV64340_I2C_STATUS_BAUDE_RATE                               0xc00C
-#define MV64340_I2C_SOFT_RESET                                      0xc01c
+#define MV64XXX_I2C_CTLR_NAME					"mv64xxx i2c"
+#define MV64XXX_I2C_OFFSET                                          0xc000
+#define MV64XXX_I2C_REG_BLOCK_SIZE                                  0x0020
 
 /****************************************/
 /* GPP Interface Registers              */
@@ -1083,6 +1080,14 @@
 	u8	brg_can_tune;
 	u8	brg_clk_src;
 	u32	brg_clk_freq;
+};
+
+/* i2c Platform Device, Driver Data */
+struct mv64xxx_i2c_pdata {
+	u32	freq_m;
+	u32	freq_n;
+	u32	timeout;	/* In milliseconds */
+	u32	retries;
 };
 
 #endif /* __ASM_MV64340_H */

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

* Re: [PATCH][I2C] Marvell mv64xxx i2c driver
       [not found] <200502020315.14281.adobriyan@mail.ru>
  2005-02-02  1:27 ` Greg KH
@ 2005-02-02 17:26 ` Mark A. Greer
       [not found]   ` <200502031556.59319.adobriyan@mail.ru>
  1 sibling, 1 reply; 23+ messages in thread
From: Mark A. Greer @ 2005-02-02 17:26 UTC (permalink / raw)
  To: Alexey Dobriyan; +Cc: Greg KH, phil, sensors, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 1309 bytes --]

Alexey Dobriyan wrote:

>On Tue, 01 Feb 2005 10:54:32 -0700, Mark A. Greer wrote:
>
>  
>
>>+struct mv64xxx_i2c_data {
>>+	void			*reg_base;
>>    
>>
>
>ioremap() returns "void __iomem *".
>

Okay.

>  
>
>>+static void __devexit
>>+mv64xxx_i2c_unmap_regs(struct mv64xxx_i2c_data *drv_data)
>>+{
>>+	if (!drv_data->reg_base) {
>>+		iounmap(drv_data->reg_base);
>>    
>>
>
>A typo? You're unmapping known to be invalid address.
>

Yes, a typo.  Good catch.

>  
>
>>+	drv_data->reg_base = 0;
>>    
>>
>
>Use NULL because drv_data->reg_base is a pointer.
>

Done.

>  
>
>>+	if ((pd->id == 0) && pdata) {
>>    
>>
>
>Rewrite this as:
>
>	if ((pd->id != 0) || !pdata)
>		return -ENODEV;
>
>and save one level of indentation below.
>

Done.

>  
>
> <snip of xxx_probe modifications>


Done.

>
>					Alexey
>
>P. S.: struct mv64xxx_i2c_data revisited...
>
>  
>
>>+	uint			state;
>>+	ulong			reg_base_p;
>>    
>>
>
>Silly request, but... Maybe this should be changed to plain old "unsigned int"
>and "unsigned long". Please. I just don't understand why people use "uint",
>"u_int", "uInt", "UINT", "uINT", "uint_t" which are always typedef'ed to
>"unsigned int".
>
>  
>

Thanks Alexey & Greg.  How's this (a complete replacement for previous 
patch)?

Signed-off-by: Mark A. Greer <mgreer@mvista.com>
--



[-- Attachment #2: i2c_5.patch --]
[-- Type: text/plain, Size: 20962 bytes --]

diff -Nru a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
--- a/drivers/i2c/busses/Kconfig	2005-02-02 10:24:30 -07:00
+++ b/drivers/i2c/busses/Kconfig	2005-02-02 10:24:30 -07:00
@@ -476,4 +476,14 @@
 	  This driver can also be built as a module.  If so, the module
 	  will be called i2c-pca-isa.
 
+config I2C_MV64XXX
+	tristate "Marvell mv64xxx I2C Controller"
+	depends on I2C && MV64X60 && EXPERIMENTAL
+	help
+	  If you say yes to this option, support will be included for the
+	  built-in I2C interface on the Marvell 64xxx line of host bridges
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called i2c-mv64xxx.
+
 endmenu
diff -Nru a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
--- a/drivers/i2c/busses/Makefile	2005-02-02 10:24:30 -07:00
+++ b/drivers/i2c/busses/Makefile	2005-02-02 10:24:30 -07:00
@@ -20,6 +20,7 @@
 obj-$(CONFIG_I2C_IXP4XX)	+= i2c-ixp4xx.o
 obj-$(CONFIG_I2C_KEYWEST)	+= i2c-keywest.o
 obj-$(CONFIG_I2C_MPC)		+= i2c-mpc.o
+obj-$(CONFIG_I2C_MV64XXX)	+= i2c-mv64xxx.o
 obj-$(CONFIG_I2C_NFORCE2)	+= i2c-nforce2.o
 obj-$(CONFIG_I2C_PARPORT)	+= i2c-parport.o
 obj-$(CONFIG_I2C_PARPORT_LIGHT)	+= i2c-parport-light.o
diff -Nru a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/drivers/i2c/busses/i2c-mv64xxx.c	2005-02-02 10:24:30 -07:00
@@ -0,0 +1,615 @@
+/*
+ * drivers/i2c/busses/i2c-mv64xxx.c
+ * 
+ * Driver for the i2c controller on the Marvell line of host bridges for MIPS
+ * and PPC (e.g, gt642[46]0, mv643[46]0, mv644[46]0).
+ *
+ * Author: Mark A. Greer <mgreer@mvista.com>
+ *
+ * 2005 (c) MontaVista, Software, Inc.  This file is licensed under
+ * the terms of the GNU General Public License version 2.  This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/mv643xx.h>
+#include <asm/io.h>
+
+/* Register defines */
+#define	MV64XXX_I2C_REG_SLAVE_ADDR			0x00
+#define	MV64XXX_I2C_REG_DATA				0x04
+#define	MV64XXX_I2C_REG_CONTROL				0x08
+#define	MV64XXX_I2C_REG_STATUS				0x0c
+#define	MV64XXX_I2C_REG_BAUD				0x0c
+#define	MV64XXX_I2C_REG_EXT_SLAVE_ADDR			0x10
+#define	MV64XXX_I2C_REG_SOFT_RESET			0x1c
+
+#define	MV64XXX_I2C_REG_CONTROL_ACK			0x00000004
+#define	MV64XXX_I2C_REG_CONTROL_IFLG			0x00000008
+#define	MV64XXX_I2C_REG_CONTROL_STOP			0x00000010
+#define	MV64XXX_I2C_REG_CONTROL_START			0x00000020
+#define	MV64XXX_I2C_REG_CONTROL_TWSIEN			0x00000040
+#define	MV64XXX_I2C_REG_CONTROL_INTEN			0x00000080
+
+/* Ctlr status values */
+#define	MV64XXX_I2C_STATUS_BUS_ERR			0x00
+#define	MV64XXX_I2C_STATUS_MAST_START			0x08
+#define	MV64XXX_I2C_STATUS_MAST_REPEAT_START		0x10
+#define	MV64XXX_I2C_STATUS_MAST_WR_ADDR_ACK		0x18
+#define	MV64XXX_I2C_STATUS_MAST_WR_ADDR_NO_ACK		0x20
+#define	MV64XXX_I2C_STATUS_MAST_WR_ACK			0x28
+#define	MV64XXX_I2C_STATUS_MAST_WR_NO_ACK		0x30
+#define	MV64XXX_I2C_STATUS_MAST_LOST_ARB		0x38
+#define	MV64XXX_I2C_STATUS_MAST_RD_ADDR_ACK		0x40
+#define	MV64XXX_I2C_STATUS_MAST_RD_ADDR_NO_ACK		0x48
+#define	MV64XXX_I2C_STATUS_MAST_RD_DATA_ACK		0x50
+#define	MV64XXX_I2C_STATUS_MAST_RD_DATA_NO_ACK		0x58
+#define	MV64XXX_I2C_STATUS_MAST_WR_ADDR_2_ACK		0xd0
+#define	MV64XXX_I2C_STATUS_MAST_WR_ADDR_2_NO_ACK	0xd8
+#define	MV64XXX_I2C_STATUS_MAST_RD_ADDR_2_ACK		0xe0
+#define	MV64XXX_I2C_STATUS_MAST_RD_ADDR_2_NO_ACK	0xe8
+#define	MV64XXX_I2C_STATUS_NO_STATUS			0xf8
+
+/* Driver states */
+enum {
+	MV64XXX_I2C_STATE_INVALID,
+	MV64XXX_I2C_STATE_IDLE,
+	MV64XXX_I2C_STATE_WAITING_FOR_START_COND,
+	MV64XXX_I2C_STATE_WAITING_FOR_ADDR_1_ACK,
+	MV64XXX_I2C_STATE_WAITING_FOR_ADDR_2_ACK,
+	MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_ACK,
+	MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_DATA,
+	MV64XXX_I2C_STATE_ABORTING,
+};
+
+/* Driver actions */
+enum {
+	MV64XXX_I2C_ACTION_INVALID,
+	MV64XXX_I2C_ACTION_CONTINUE,
+	MV64XXX_I2C_ACTION_SEND_START,
+	MV64XXX_I2C_ACTION_SEND_ADDR_1,
+	MV64XXX_I2C_ACTION_SEND_ADDR_2,
+	MV64XXX_I2C_ACTION_SEND_DATA,
+	MV64XXX_I2C_ACTION_RCV_DATA,
+	MV64XXX_I2C_ACTION_RCV_DATA_STOP,
+	MV64XXX_I2C_ACTION_SEND_STOP,
+};
+
+struct mv64xxx_i2c_data {
+	int			irq;
+	u32			state;
+	u32			action;
+	u32			cntl_bits;
+	void __iomem		*reg_base;
+	u32			reg_base_p;
+	u32			addr1;
+	u32			addr2;
+	u32			bytes_left;
+	u32			byte_posn;
+	u32			block;
+	int			rc;
+	u32			freq_m;
+	u32			freq_n;
+	wait_queue_head_t	waitq;
+	spinlock_t		lock;
+	struct i2c_msg		*msg;
+	struct i2c_adapter	adapter;
+};
+
+/*
+ *****************************************************************************
+ *
+ *	Finite State Machine & Interrupt Routines
+ *
+ *****************************************************************************
+ */
+static void
+mv64xxx_i2c_fsm(struct mv64xxx_i2c_data *drv_data, u32 status)
+{
+	/*
+	 * If state is idle, then this is likely the remnants of an old
+	 * operation that driver has given up on or the user has killed.
+	 * If so, issue the stop condition and go to idle.
+	 */
+	if (drv_data->state == MV64XXX_I2C_STATE_IDLE) {
+		drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
+		drv_data->state = MV64XXX_I2C_STATE_IDLE;
+		return;
+	}
+
+	if (drv_data->state == MV64XXX_I2C_STATE_ABORTING) {
+		drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
+		drv_data->state = MV64XXX_I2C_STATE_IDLE;
+		return;
+	}
+
+	/* The status from the ctlr [mostly] tells us what to do next */
+	switch (status) {
+	/* Start condition interrupt */
+	case MV64XXX_I2C_STATUS_MAST_START: /* 0x08 */
+	case MV64XXX_I2C_STATUS_MAST_REPEAT_START: /* 0x10 */
+		drv_data->action = MV64XXX_I2C_ACTION_SEND_ADDR_1;
+		drv_data->state = MV64XXX_I2C_STATE_WAITING_FOR_ADDR_1_ACK;
+		break;
+
+	/* Performing a write */
+	case MV64XXX_I2C_STATUS_MAST_WR_ADDR_ACK: /* 0x18 */
+		if (drv_data->msg->flags & I2C_M_TEN) {
+			drv_data->action = MV64XXX_I2C_ACTION_SEND_ADDR_2;
+			drv_data->state =
+				MV64XXX_I2C_STATE_WAITING_FOR_ADDR_2_ACK;
+			break;
+		}
+		/* FALLTHRU */
+	case MV64XXX_I2C_STATUS_MAST_WR_ADDR_2_ACK: /* 0xd0 */
+	case MV64XXX_I2C_STATUS_MAST_WR_ACK: /* 0x28 */
+		if (drv_data->bytes_left > 0) {
+			drv_data->action = MV64XXX_I2C_ACTION_SEND_DATA;
+			drv_data->state =
+				MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_ACK;
+			drv_data->bytes_left--;
+		}
+		else {
+			drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
+			drv_data->state = MV64XXX_I2C_STATE_IDLE;
+		}
+		break;
+
+	/* Performing a read */
+	case MV64XXX_I2C_STATUS_MAST_RD_ADDR_ACK: /* 40 */
+		if (drv_data->msg->flags & I2C_M_TEN) {
+			drv_data->action = MV64XXX_I2C_ACTION_SEND_ADDR_2;
+			drv_data->state =
+				MV64XXX_I2C_STATE_WAITING_FOR_ADDR_2_ACK;
+			break;
+		}
+		/* FALLTHRU */
+	case MV64XXX_I2C_STATUS_MAST_RD_ADDR_2_ACK: /* 0xe0 */
+		if (drv_data->bytes_left == 0) {
+			drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
+			drv_data->state = MV64XXX_I2C_STATE_IDLE;
+			break;
+		}
+		/* FALLTHRU */
+	case MV64XXX_I2C_STATUS_MAST_RD_DATA_ACK: /* 0x50 */
+		if (status != MV64XXX_I2C_STATUS_MAST_RD_DATA_ACK)
+			drv_data->action = MV64XXX_I2C_ACTION_CONTINUE;
+		else {
+			drv_data->action = MV64XXX_I2C_ACTION_RCV_DATA;
+			drv_data->bytes_left--;
+		}
+		drv_data->state = MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_DATA;
+
+		if (drv_data->bytes_left == 1)
+			drv_data->cntl_bits &= ~MV64XXX_I2C_REG_CONTROL_ACK;
+		break;
+
+	case MV64XXX_I2C_STATUS_MAST_RD_DATA_NO_ACK: /* 0x58 */
+		drv_data->action = MV64XXX_I2C_ACTION_RCV_DATA_STOP;
+		drv_data->state = MV64XXX_I2C_STATE_IDLE;
+		break;
+
+	case MV64XXX_I2C_STATUS_MAST_WR_ADDR_NO_ACK: /* 0x20 */
+	case MV64XXX_I2C_STATUS_MAST_WR_NO_ACK: /* 30 */
+	case MV64XXX_I2C_STATUS_MAST_RD_ADDR_NO_ACK: /* 48 */
+		/* Doesn't seem to be a device at other end */
+		drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
+		drv_data->state = MV64XXX_I2C_STATE_IDLE;
+		drv_data->rc = -ENODEV;
+		break;
+
+	default:
+		dev_err(&drv_data->adapter.dev,
+			"mv64xxx_i2c_fsm: Ctlr Error -- state: 0x%x, "
+			"status: 0x%x, addr: 0x%x, flags: 0x%x\n",
+			 drv_data->state, status, drv_data->msg->addr,
+			 drv_data->msg->flags);
+		drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
+		drv_data->state = MV64XXX_I2C_STATE_IDLE;
+		drv_data->rc = -EIO;
+	}
+
+	return;
+}
+
+static void
+mv64xxx_i2c_do_action(struct mv64xxx_i2c_data *drv_data)
+{
+	switch(drv_data->action) {
+	case MV64XXX_I2C_ACTION_CONTINUE:
+		writel(drv_data->cntl_bits,
+			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+		break;
+
+	case MV64XXX_I2C_ACTION_SEND_START:
+		writel(drv_data->cntl_bits | MV64XXX_I2C_REG_CONTROL_START,
+			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+		break;
+
+	case MV64XXX_I2C_ACTION_SEND_ADDR_1:
+		writel(drv_data->addr1,
+			drv_data->reg_base + MV64XXX_I2C_REG_DATA);
+		writel(drv_data->cntl_bits,
+			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+		break;
+
+	case MV64XXX_I2C_ACTION_SEND_ADDR_2:
+		writel(drv_data->addr2,
+			drv_data->reg_base + MV64XXX_I2C_REG_DATA);
+		writel(drv_data->cntl_bits,
+			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+		break;
+
+	case MV64XXX_I2C_ACTION_SEND_DATA:
+		writel(drv_data->msg->buf[drv_data->byte_posn++],
+			drv_data->reg_base + MV64XXX_I2C_REG_DATA);
+		writel(drv_data->cntl_bits,
+			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+		break;
+
+	case MV64XXX_I2C_ACTION_RCV_DATA:
+		drv_data->msg->buf[drv_data->byte_posn++] =
+			readl(drv_data->reg_base + MV64XXX_I2C_REG_DATA);
+		writel(drv_data->cntl_bits,
+			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+		break;
+
+	case MV64XXX_I2C_ACTION_RCV_DATA_STOP:
+		drv_data->msg->buf[drv_data->byte_posn++] =
+			readl(drv_data->reg_base + MV64XXX_I2C_REG_DATA);
+		drv_data->cntl_bits &= ~MV64XXX_I2C_REG_CONTROL_INTEN;
+		writel(drv_data->cntl_bits | MV64XXX_I2C_REG_CONTROL_STOP,
+			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+		drv_data->block = 0;
+		wake_up_interruptible(&drv_data->waitq);
+		break;
+
+	case MV64XXX_I2C_ACTION_INVALID:
+	default:
+		dev_err(&drv_data->adapter.dev,
+			"mv64xxx_i2c_do_action: Invalid action: %d\n",
+			drv_data->action);
+		drv_data->rc = -EIO;
+		/* FALLTHRU */
+	case MV64XXX_I2C_ACTION_SEND_STOP:
+		drv_data->cntl_bits &= ~MV64XXX_I2C_REG_CONTROL_INTEN;
+		writel(drv_data->cntl_bits | MV64XXX_I2C_REG_CONTROL_STOP,
+			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+		drv_data->block = 0;
+		wake_up_interruptible(&drv_data->waitq);
+		break;
+	}
+
+	return;
+}
+
+static int
+mv64xxx_i2c_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct mv64xxx_i2c_data	*drv_data = dev_id;
+	u32	status;
+	long	flags;
+	int	rc = IRQ_NONE;
+
+	spin_lock_irqsave(&drv_data->lock, flags);
+	while (readl(drv_data->reg_base + MV64XXX_I2C_REG_CONTROL) &
+						MV64XXX_I2C_REG_CONTROL_IFLG) {
+		status = readl(drv_data->reg_base + MV64XXX_I2C_REG_STATUS);
+		mv64xxx_i2c_fsm(drv_data, status);
+		mv64xxx_i2c_do_action(drv_data);
+		rc = IRQ_HANDLED;
+	}
+	spin_unlock_irqrestore(&drv_data->lock, flags);
+
+	return rc;
+}
+
+/*
+ *****************************************************************************
+ *
+ *	I2C Msg Execution Routines
+ *
+ *****************************************************************************
+ */
+static void
+mv64xxx_i2c_prepare_for_io(struct mv64xxx_i2c_data *drv_data,
+	struct i2c_msg *msg)
+{
+	u32	dir = 0;
+
+	drv_data->msg = msg;
+	drv_data->byte_posn = 0;
+	drv_data->bytes_left = msg->len;
+	drv_data->rc = 0;
+	drv_data->cntl_bits = MV64XXX_I2C_REG_CONTROL_ACK |
+		MV64XXX_I2C_REG_CONTROL_INTEN | MV64XXX_I2C_REG_CONTROL_TWSIEN;
+
+	if (msg->flags & I2C_M_RD)
+		dir = 1;
+
+	if (msg->flags & I2C_M_REV_DIR_ADDR)
+		dir ^= 1;
+
+	if (msg->flags & I2C_M_TEN) {
+		drv_data->addr1 = 0xf0 | (((u32)msg->addr & 0x300) >> 7) | dir;
+		drv_data->addr2 = (u32)msg->addr & 0xff;
+	}
+	else {
+		drv_data->addr1 = ((u32)msg->addr & 0x7f) << 1 | dir;
+		drv_data->addr2 = 0;
+	}
+
+	return;
+}
+
+static void
+mv64xxx_i2c_wait_for_completion(struct mv64xxx_i2c_data *drv_data)
+{
+	long	flags, time_left;
+	char	abort = 0;
+
+	time_left = wait_event_interruptible_timeout(drv_data->waitq,
+		!drv_data->block, msecs_to_jiffies(drv_data->adapter.timeout));
+
+	spin_lock_irqsave(&drv_data->lock, flags);
+	if (!time_left) { /* Timed out */
+		drv_data->rc = -ETIMEDOUT;
+		abort = 1;
+	}
+	else if (time_left < 0) { /* Interrupted/Error */
+		drv_data->rc = time_left; /* errno value */
+		abort = 1;
+	}
+
+	if (abort && drv_data->block) {
+		drv_data->state = MV64XXX_I2C_STATE_ABORTING;
+		spin_unlock_irqrestore(&drv_data->lock, flags);
+
+		time_left = wait_event_timeout(drv_data->waitq,
+			!drv_data->block,
+			msecs_to_jiffies(drv_data->adapter.timeout));
+
+		if (!time_left <= 0) {
+			drv_data->state = MV64XXX_I2C_STATE_IDLE;
+			dev_err(&drv_data->adapter.dev,
+				"mv64xxx: I2C bus locked\n");
+		}
+	}
+	else
+		spin_unlock_irqrestore(&drv_data->lock, flags);
+
+	return;
+}
+
+static int
+mv64xxx_i2c_execute_msg(struct mv64xxx_i2c_data *drv_data, struct i2c_msg *msg)
+{
+	long	flags;
+
+	spin_lock_irqsave(&drv_data->lock, flags);
+	mv64xxx_i2c_prepare_for_io(drv_data, msg);
+
+	if (unlikely(msg->flags & I2C_M_NOSTART)) { /* Skip start/addr phases */
+		if (drv_data->msg->flags & I2C_M_RD) {
+			/* No action to do, wait for slave to send a byte */
+			drv_data->action = MV64XXX_I2C_ACTION_CONTINUE;
+			drv_data->state =
+				MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_DATA;
+		}
+		else {
+			drv_data->action = MV64XXX_I2C_ACTION_SEND_DATA;
+			drv_data->state =
+				MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_ACK;
+			drv_data->bytes_left--;
+		}
+	}
+	else {
+		drv_data->action = MV64XXX_I2C_ACTION_SEND_START;
+		drv_data->state = MV64XXX_I2C_STATE_WAITING_FOR_START_COND;
+	}
+
+	drv_data->block = 1;
+	mv64xxx_i2c_do_action(drv_data);
+	spin_unlock_irqrestore(&drv_data->lock, flags);
+
+	mv64xxx_i2c_wait_for_completion(drv_data);
+	return drv_data->rc;
+}
+
+/*
+ *****************************************************************************
+ *
+ *	I2C Core Support Routines (Interface to higher level I2C code)
+ *
+ *****************************************************************************
+ */
+static u32
+mv64xxx_i2c_functionality(struct i2c_adapter *adap)
+{
+	return I2C_FUNC_I2C |I2C_FUNC_10BIT_ADDR | I2C_FUNC_SMBUS_EMUL;
+}
+
+static int
+mv64xxx_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
+{
+	struct mv64xxx_i2c_data *drv_data = i2c_get_adapdata(adap);
+	int	i, rc = 0;
+
+	for (i=0; i<num; i++)
+		if ((rc = mv64xxx_i2c_execute_msg(drv_data, &msgs[i])) != 0)
+			break;
+
+	return rc;
+}
+
+static struct i2c_algorithm mv64xxx_i2c_algo = {
+	.name = MV64XXX_I2C_CTLR_NAME "algorithm",
+	.id = I2C_ALGO_MV64XXX,
+	.master_xfer = mv64xxx_i2c_xfer,
+	.functionality = mv64xxx_i2c_functionality,
+};
+
+/*
+ *****************************************************************************
+ *
+ *	Driver Interface & Early Init Routines
+ *
+ *****************************************************************************
+ */
+static void __devinit
+mv64xxx_i2c_hw_init(struct mv64xxx_i2c_data *drv_data)
+{
+	writel(0, drv_data->reg_base + MV64XXX_I2C_REG_SOFT_RESET);
+	writel((((drv_data->freq_m & 0xf) << 3) | (drv_data->freq_n & 0x7)),
+		drv_data->reg_base + MV64XXX_I2C_REG_BAUD);
+	writel(0, drv_data->reg_base + MV64XXX_I2C_REG_SLAVE_ADDR);
+	writel(0, drv_data->reg_base + MV64XXX_I2C_REG_EXT_SLAVE_ADDR);
+	writel(MV64XXX_I2C_REG_CONTROL_TWSIEN | MV64XXX_I2C_REG_CONTROL_STOP,
+		drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+	drv_data->state = MV64XXX_I2C_STATE_IDLE;
+	return;
+}
+
+static int __devinit
+mv64xxx_i2c_map_regs(struct platform_device *pd,
+	struct mv64xxx_i2c_data *drv_data)
+{
+	struct resource	*r;
+
+	if ((r = platform_get_resource(pd, IORESOURCE_MEM, 0)) &&
+		request_mem_region(r->start, MV64XXX_I2C_REG_BLOCK_SIZE,
+			drv_data->adapter.name)) {
+
+		drv_data->reg_base = ioremap(r->start,
+			MV64XXX_I2C_REG_BLOCK_SIZE);
+		drv_data->reg_base_p = r->start;
+	}
+	else
+		return -ENOMEM;
+
+	return 0;
+}
+
+static void __devexit
+mv64xxx_i2c_unmap_regs(struct mv64xxx_i2c_data *drv_data)
+{
+	if (drv_data->reg_base) {
+		iounmap(drv_data->reg_base);
+		release_mem_region(drv_data->reg_base_p,
+			MV64XXX_I2C_REG_BLOCK_SIZE);
+	}
+
+	drv_data->reg_base = NULL;
+	drv_data->reg_base_p = 0;
+	return;
+}
+
+static int __devinit
+mv64xxx_i2c_probe(struct device *dev)
+{
+	struct platform_device		*pd = to_platform_device(dev);
+	struct mv64xxx_i2c_data		*drv_data;
+	struct mv64xxx_i2c_pdata	*pdata = dev->platform_data;
+	int	rc;
+
+	if ((pd->id != 0) || !pdata)
+		return -ENODEV;
+
+	drv_data = kmalloc(sizeof(struct mv64xxx_i2c_data), GFP_KERNEL);
+
+	if (!drv_data)
+		return -ENOMEM;
+
+	memset(drv_data, 0, sizeof(struct mv64xxx_i2c_data));
+
+	if (mv64xxx_i2c_map_regs(pd, drv_data)) {
+		rc = -ENODEV;
+		goto exit_kfree;
+	}
+
+	strncpy(drv_data->adapter.name, MV64XXX_I2C_CTLR_NAME "adapter",
+		I2C_NAME_SIZE);
+
+	init_waitqueue_head(&drv_data->waitq);
+	spin_lock_init(&drv_data->lock);
+
+	drv_data->freq_m = pdata->freq_m;
+	drv_data->freq_n = pdata->freq_n;
+	drv_data->irq = platform_get_irq(pd, 0);
+	drv_data->adapter.id = I2C_ALGO_MV64XXX | I2C_HW_MV64XXX;
+	drv_data->adapter.algo = &mv64xxx_i2c_algo;
+	drv_data->adapter.timeout = pdata->timeout;
+	drv_data->adapter.retries = pdata->retries;
+	dev_set_drvdata(dev, drv_data);
+	i2c_set_adapdata(&drv_data->adapter, drv_data);
+
+	if (request_irq(drv_data->irq, mv64xxx_i2c_intr, 0,
+		MV64XXX_I2C_CTLR_NAME, drv_data)) {
+
+		dev_err(dev, "mv64xxx: Can't register intr handler "
+			"irq: %d\\n", drv_data->irq);
+		rc = -EINVAL;
+		goto exit_unmap_regs;
+	}
+	else if ((rc = i2c_add_adapter(&drv_data->adapter)) != 0) {
+		dev_err(dev, "mv64xxx: Can't add i2c adapter, rc: %d\n", -rc);
+		goto exit_free_irq;
+	}
+
+	mv64xxx_i2c_hw_init(drv_data);
+
+	return 0;
+
+	exit_free_irq:
+		free_irq(drv_data->irq, drv_data);
+	exit_unmap_regs:
+		mv64xxx_i2c_unmap_regs(drv_data);
+	exit_kfree:
+		kfree(drv_data);
+	return rc;
+}
+
+static int __devexit
+mv64xxx_i2c_remove(struct device *dev)
+{
+	struct mv64xxx_i2c_data		*drv_data = dev_get_drvdata(dev);
+	int	rc;
+
+	rc = i2c_del_adapter(&drv_data->adapter);
+	free_irq(drv_data->irq, drv_data);
+	mv64xxx_i2c_unmap_regs(drv_data);
+	kfree(drv_data);
+
+	return rc;
+}
+
+static struct device_driver mv64xxx_i2c_driver = {
+	.name	= MV64XXX_I2C_CTLR_NAME,
+	.bus	= &platform_bus_type,
+	.probe	= mv64xxx_i2c_probe,
+	.remove	= mv64xxx_i2c_remove,
+};
+
+static int __devinit
+mv64xxx_i2c_init(void)
+{
+	return driver_register(&mv64xxx_i2c_driver);
+}
+
+static void __devexit
+mv64xxx_i2c_exit(void)
+{
+	driver_unregister(&mv64xxx_i2c_driver);
+	return;
+}
+
+module_init(mv64xxx_i2c_init);
+module_exit(mv64xxx_i2c_exit);
+
+MODULE_AUTHOR("Mark A. Greer <mgreer@mvista.com>");
+MODULE_DESCRIPTION("Marvell mv64xxx host bridge i2c ctlr driver");
+MODULE_LICENSE("GPL");
diff -Nru a/include/linux/i2c-id.h b/include/linux/i2c-id.h
--- a/include/linux/i2c-id.h	2005-02-02 10:24:30 -07:00
+++ b/include/linux/i2c-id.h	2005-02-02 10:24:30 -07:00
@@ -200,6 +200,9 @@
 
 #define I2C_ALGO_SIBYTE 0x150000	/* Broadcom SiByte SOCs		*/
 #define I2C_ALGO_SGI	0x160000        /* SGI algorithm                */
+					/* 0x170000 - USB		*/
+					/* 0x180000 - Virtual buses	*/
+#define I2C_ALGO_MV64XXX 0x190000       /* Marvell mv64xxx i2c ctlr	*/
 
 #define I2C_ALGO_EXP	0x800000	/* experimental			*/
 
@@ -305,5 +308,8 @@
 
 /* --- MCP107 adapter */
 #define I2C_HW_MPC107 0x00
+
+/* --- Marvell mv64xxx i2c adapter */
+#define I2C_HW_MV64XXX 0x00
 
 #endif /* LINUX_I2C_ID_H */
diff -Nru a/include/linux/mv643xx.h b/include/linux/mv643xx.h
--- a/include/linux/mv643xx.h	2005-02-02 10:24:30 -07:00
+++ b/include/linux/mv643xx.h	2005-02-02 10:24:30 -07:00
@@ -977,12 +977,9 @@
 /* I2C Registers                        */
 /****************************************/
 
-#define MV64340_I2C_SLAVE_ADDR                                      0xc000
-#define MV64340_I2C_EXTENDED_SLAVE_ADDR                             0xc010
-#define MV64340_I2C_DATA                                            0xc004
-#define MV64340_I2C_CONTROL                                         0xc008
-#define MV64340_I2C_STATUS_BAUDE_RATE                               0xc00C
-#define MV64340_I2C_SOFT_RESET                                      0xc01c
+#define MV64XXX_I2C_CTLR_NAME					"mv64xxx i2c"
+#define MV64XXX_I2C_OFFSET                                          0xc000
+#define MV64XXX_I2C_REG_BLOCK_SIZE                                  0x0020
 
 /****************************************/
 /* GPP Interface Registers              */
@@ -1083,6 +1080,14 @@
 	u8	brg_can_tune;
 	u8	brg_clk_src;
 	u32	brg_clk_freq;
+};
+
+/* i2c Platform Device, Driver Data */
+struct mv64xxx_i2c_pdata {
+	u32	freq_m;
+	u32	freq_n;
+	u32	timeout;	/* In milliseconds */
+	u32	retries;
 };
 
 #endif /* __ASM_MV64340_H */

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

* Re: [PATCH][I2C] Marvell mv64xxx i2c driver
       [not found] <200502020315.14281.adobriyan@mail.ru>
@ 2005-02-02  1:27 ` Greg KH
  2005-02-02 17:26 ` Mark A. Greer
  1 sibling, 0 replies; 23+ messages in thread
From: Greg KH @ 2005-02-02  1:27 UTC (permalink / raw)
  To: Alexey Dobriyan; +Cc: Mark A. Greer, phil, sensors, linux-kernel

On Wed, Feb 02, 2005 at 03:15:14AM +0200, Alexey Dobriyan wrote:
 
> P. S.: struct mv64xxx_i2c_data revisited...
> 
> > +	uint			state;
> > +	ulong			reg_base_p;
> 
> Silly request, but... Maybe this should be changed to plain old "unsigned int"
> and "unsigned long". Please. I just don't understand why people use "uint",
> "u_int", "uInt", "UINT", "uINT", "uint_t" which are always typedef'ed to
> "unsigned int".

Not a silly request at all.  Please use the u32, u64 and so on values
instead.  That way we know what you mean, and it's portable.

thanks,

greg k-h

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

* Re: [PATCH][I2C] Marvell mv64xxx i2c driver
  2005-02-01  0:46     ` Greg KH
@ 2005-02-01 17:54       ` Mark A. Greer
  0 siblings, 0 replies; 23+ messages in thread
From: Mark A. Greer @ 2005-02-01 17:54 UTC (permalink / raw)
  To: Greg KH; +Cc: phil, sensors, lkml

[-- Attachment #1: Type: text/plain, Size: 114 bytes --]

Greg KH wrote:

>How about a whole new patch that I could apply?  That would be better :)
>  
>

Oops, sorry.  :)

[-- Attachment #2: i2c_4.patch --]
[-- Type: text/plain, Size: 20976 bytes --]

diff -Nru a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
--- a/drivers/i2c/busses/Kconfig	2005-02-01 10:45:00 -07:00
+++ b/drivers/i2c/busses/Kconfig	2005-02-01 10:45:00 -07:00
@@ -476,4 +476,14 @@
 	  This driver can also be built as a module.  If so, the module
 	  will be called i2c-pca-isa.
 
+config I2C_MV64XXX
+	tristate "Marvell mv64xxx I2C Controller"
+	depends on I2C && MV64X60 && EXPERIMENTAL
+	help
+	  If you say yes to this option, support will be included for the
+	  built-in I2C interface on the Marvell 64xxx line of host bridges
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called i2c-mv64xxx.
+
 endmenu
diff -Nru a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
--- a/drivers/i2c/busses/Makefile	2005-02-01 10:45:00 -07:00
+++ b/drivers/i2c/busses/Makefile	2005-02-01 10:45:00 -07:00
@@ -20,6 +20,7 @@
 obj-$(CONFIG_I2C_IXP4XX)	+= i2c-ixp4xx.o
 obj-$(CONFIG_I2C_KEYWEST)	+= i2c-keywest.o
 obj-$(CONFIG_I2C_MPC)		+= i2c-mpc.o
+obj-$(CONFIG_I2C_MV64XXX)	+= i2c-mv64xxx.o
 obj-$(CONFIG_I2C_NFORCE2)	+= i2c-nforce2.o
 obj-$(CONFIG_I2C_PARPORT)	+= i2c-parport.o
 obj-$(CONFIG_I2C_PARPORT_LIGHT)	+= i2c-parport-light.o
diff -Nru a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/drivers/i2c/busses/i2c-mv64xxx.c	2005-02-01 10:45:00 -07:00
@@ -0,0 +1,614 @@
+/*
+ * drivers/i2c/busses/i2c-mv64xxx.c
+ * 
+ * Driver for the i2c controller on the Marvell line of host bridges for MIPS
+ * and PPC (e.g, gt642[46]0, mv643[46]0, mv644[46]0).
+ *
+ * Author: Mark A. Greer <mgreer@mvista.com>
+ *
+ * 2005 (c) MontaVista, Software, Inc.  This file is licensed under
+ * the terms of the GNU General Public License version 2.  This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/mv643xx.h>
+#include <asm/io.h>
+
+/* Register defines */
+#define	MV64XXX_I2C_REG_SLAVE_ADDR			0x00
+#define	MV64XXX_I2C_REG_DATA				0x04
+#define	MV64XXX_I2C_REG_CONTROL				0x08
+#define	MV64XXX_I2C_REG_STATUS				0x0c
+#define	MV64XXX_I2C_REG_BAUD				0x0c
+#define	MV64XXX_I2C_REG_EXT_SLAVE_ADDR			0x10
+#define	MV64XXX_I2C_REG_SOFT_RESET			0x1c
+
+#define	MV64XXX_I2C_REG_CONTROL_ACK			0x00000004
+#define	MV64XXX_I2C_REG_CONTROL_IFLG			0x00000008
+#define	MV64XXX_I2C_REG_CONTROL_STOP			0x00000010
+#define	MV64XXX_I2C_REG_CONTROL_START			0x00000020
+#define	MV64XXX_I2C_REG_CONTROL_TWSIEN			0x00000040
+#define	MV64XXX_I2C_REG_CONTROL_INTEN			0x00000080
+
+/* Ctlr status values */
+#define	MV64XXX_I2C_STATUS_BUS_ERR			0x00
+#define	MV64XXX_I2C_STATUS_MAST_START			0x08
+#define	MV64XXX_I2C_STATUS_MAST_REPEAT_START		0x10
+#define	MV64XXX_I2C_STATUS_MAST_WR_ADDR_ACK		0x18
+#define	MV64XXX_I2C_STATUS_MAST_WR_ADDR_NO_ACK		0x20
+#define	MV64XXX_I2C_STATUS_MAST_WR_ACK			0x28
+#define	MV64XXX_I2C_STATUS_MAST_WR_NO_ACK		0x30
+#define	MV64XXX_I2C_STATUS_MAST_LOST_ARB		0x38
+#define	MV64XXX_I2C_STATUS_MAST_RD_ADDR_ACK		0x40
+#define	MV64XXX_I2C_STATUS_MAST_RD_ADDR_NO_ACK		0x48
+#define	MV64XXX_I2C_STATUS_MAST_RD_DATA_ACK		0x50
+#define	MV64XXX_I2C_STATUS_MAST_RD_DATA_NO_ACK		0x58
+#define	MV64XXX_I2C_STATUS_MAST_WR_ADDR_2_ACK		0xd0
+#define	MV64XXX_I2C_STATUS_MAST_WR_ADDR_2_NO_ACK	0xd8
+#define	MV64XXX_I2C_STATUS_MAST_RD_ADDR_2_ACK		0xe0
+#define	MV64XXX_I2C_STATUS_MAST_RD_ADDR_2_NO_ACK	0xe8
+#define	MV64XXX_I2C_STATUS_NO_STATUS			0xf8
+
+/* Driver states */
+enum {
+	MV64XXX_I2C_STATE_INVALID,
+	MV64XXX_I2C_STATE_IDLE,
+	MV64XXX_I2C_STATE_WAITING_FOR_START_COND,
+	MV64XXX_I2C_STATE_WAITING_FOR_ADDR_1_ACK,
+	MV64XXX_I2C_STATE_WAITING_FOR_ADDR_2_ACK,
+	MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_ACK,
+	MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_DATA,
+	MV64XXX_I2C_STATE_ABORTING,
+};
+
+/* Driver actions */
+enum {
+	MV64XXX_I2C_ACTION_INVALID,
+	MV64XXX_I2C_ACTION_CONTINUE,
+	MV64XXX_I2C_ACTION_SEND_START,
+	MV64XXX_I2C_ACTION_SEND_ADDR_1,
+	MV64XXX_I2C_ACTION_SEND_ADDR_2,
+	MV64XXX_I2C_ACTION_SEND_DATA,
+	MV64XXX_I2C_ACTION_RCV_DATA,
+	MV64XXX_I2C_ACTION_RCV_DATA_STOP,
+	MV64XXX_I2C_ACTION_SEND_STOP,
+};
+
+struct mv64xxx_i2c_data {
+	int			irq;
+	uint			state;
+	uint			action;
+	u32			cntl_bits;
+	void			*reg_base;
+	ulong			reg_base_p;
+	u32			addr1;
+	u32			addr2;
+	uint			bytes_left;
+	uint			byte_posn;
+	uint			block;
+	int			rc;
+	u32			freq_m;
+	u32			freq_n;
+	wait_queue_head_t	waitq;
+	spinlock_t		lock;
+	struct i2c_msg		*msg;
+	struct i2c_adapter	adapter;
+};
+
+/*
+ *****************************************************************************
+ *
+ *	Finite State Machine & Interrupt Routines
+ *
+ *****************************************************************************
+ */
+static void
+mv64xxx_i2c_fsm(struct mv64xxx_i2c_data *drv_data, u32 status)
+{
+	/*
+	 * If state is idle, then this is likely the remnants of an old
+	 * operation that driver has given up on or the user has killed.
+	 * If so, issue the stop condition and go to idle.
+	 */
+	if (drv_data->state == MV64XXX_I2C_STATE_IDLE) {
+		drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
+		drv_data->state = MV64XXX_I2C_STATE_IDLE;
+		return;
+	}
+
+	if (drv_data->state == MV64XXX_I2C_STATE_ABORTING) {
+		drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
+		drv_data->state = MV64XXX_I2C_STATE_IDLE;
+		return;
+	}
+
+	/* The status from the ctlr [mostly] tells us what to do next */
+	switch (status) {
+	/* Start condition interrupt */
+	case MV64XXX_I2C_STATUS_MAST_START: /* 0x08 */
+	case MV64XXX_I2C_STATUS_MAST_REPEAT_START: /* 0x10 */
+		drv_data->action = MV64XXX_I2C_ACTION_SEND_ADDR_1;
+		drv_data->state = MV64XXX_I2C_STATE_WAITING_FOR_ADDR_1_ACK;
+		break;
+
+	/* Performing a write */
+	case MV64XXX_I2C_STATUS_MAST_WR_ADDR_ACK: /* 0x18 */
+		if (drv_data->msg->flags & I2C_M_TEN) {
+			drv_data->action = MV64XXX_I2C_ACTION_SEND_ADDR_2;
+			drv_data->state =
+				MV64XXX_I2C_STATE_WAITING_FOR_ADDR_2_ACK;
+			break;
+		}
+		/* FALLTHRU */
+	case MV64XXX_I2C_STATUS_MAST_WR_ADDR_2_ACK: /* 0xd0 */
+	case MV64XXX_I2C_STATUS_MAST_WR_ACK: /* 0x28 */
+		if (drv_data->bytes_left > 0) {
+			drv_data->action = MV64XXX_I2C_ACTION_SEND_DATA;
+			drv_data->state =
+				MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_ACK;
+			drv_data->bytes_left--;
+		}
+		else {
+			drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
+			drv_data->state = MV64XXX_I2C_STATE_IDLE;
+		}
+		break;
+
+	/* Performing a read */
+	case MV64XXX_I2C_STATUS_MAST_RD_ADDR_ACK: /* 40 */
+		if (drv_data->msg->flags & I2C_M_TEN) {
+			drv_data->action = MV64XXX_I2C_ACTION_SEND_ADDR_2;
+			drv_data->state =
+				MV64XXX_I2C_STATE_WAITING_FOR_ADDR_2_ACK;
+			break;
+		}
+		/* FALLTHRU */
+	case MV64XXX_I2C_STATUS_MAST_RD_ADDR_2_ACK: /* 0xe0 */
+		if (drv_data->bytes_left == 0) {
+			drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
+			drv_data->state = MV64XXX_I2C_STATE_IDLE;
+			break;
+		}
+		/* FALLTHRU */
+	case MV64XXX_I2C_STATUS_MAST_RD_DATA_ACK: /* 0x50 */
+		if (status != MV64XXX_I2C_STATUS_MAST_RD_DATA_ACK)
+			drv_data->action = MV64XXX_I2C_ACTION_CONTINUE;
+		else {
+			drv_data->action = MV64XXX_I2C_ACTION_RCV_DATA;
+			drv_data->bytes_left--;
+		}
+		drv_data->state = MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_DATA;
+
+		if (drv_data->bytes_left == 1)
+			drv_data->cntl_bits &= ~MV64XXX_I2C_REG_CONTROL_ACK;
+		break;
+
+	case MV64XXX_I2C_STATUS_MAST_RD_DATA_NO_ACK: /* 0x58 */
+		drv_data->action = MV64XXX_I2C_ACTION_RCV_DATA_STOP;
+		drv_data->state = MV64XXX_I2C_STATE_IDLE;
+		break;
+
+	case MV64XXX_I2C_STATUS_MAST_WR_ADDR_NO_ACK: /* 0x20 */
+	case MV64XXX_I2C_STATUS_MAST_WR_NO_ACK: /* 30 */
+	case MV64XXX_I2C_STATUS_MAST_RD_ADDR_NO_ACK: /* 48 */
+		/* Doesn't seem to be a device at other end */
+		drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
+		drv_data->state = MV64XXX_I2C_STATE_IDLE;
+		drv_data->rc = -ENODEV;
+		break;
+
+	default:
+		dev_err(&drv_data->adapter.dev,
+			"mv64xxx_i2c_fsm: Ctlr Error -- state: 0x%x, "
+			"status: 0x%x, addr: 0x%x, flags: 0x%x\n",
+			 drv_data->state, status, drv_data->msg->addr,
+			 drv_data->msg->flags);
+		drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
+		drv_data->state = MV64XXX_I2C_STATE_IDLE;
+		drv_data->rc = -EIO;
+	}
+
+	return;
+}
+
+static void
+mv64xxx_i2c_do_action(struct mv64xxx_i2c_data *drv_data)
+{
+	switch(drv_data->action) {
+	case MV64XXX_I2C_ACTION_CONTINUE:
+		writel(drv_data->cntl_bits,
+			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+		break;
+
+	case MV64XXX_I2C_ACTION_SEND_START:
+		writel(drv_data->cntl_bits | MV64XXX_I2C_REG_CONTROL_START,
+			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+		break;
+
+	case MV64XXX_I2C_ACTION_SEND_ADDR_1:
+		writel(drv_data->addr1,
+			drv_data->reg_base + MV64XXX_I2C_REG_DATA);
+		writel(drv_data->cntl_bits,
+			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+		break;
+
+	case MV64XXX_I2C_ACTION_SEND_ADDR_2:
+		writel(drv_data->addr2,
+			drv_data->reg_base + MV64XXX_I2C_REG_DATA);
+		writel(drv_data->cntl_bits,
+			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+		break;
+
+	case MV64XXX_I2C_ACTION_SEND_DATA:
+		writel(drv_data->msg->buf[drv_data->byte_posn++],
+			drv_data->reg_base + MV64XXX_I2C_REG_DATA);
+		writel(drv_data->cntl_bits,
+			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+		break;
+
+	case MV64XXX_I2C_ACTION_RCV_DATA:
+		drv_data->msg->buf[drv_data->byte_posn++] =
+			readl(drv_data->reg_base + MV64XXX_I2C_REG_DATA);
+		writel(drv_data->cntl_bits,
+			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+		break;
+
+	case MV64XXX_I2C_ACTION_RCV_DATA_STOP:
+		drv_data->msg->buf[drv_data->byte_posn++] =
+			readl(drv_data->reg_base + MV64XXX_I2C_REG_DATA);
+		drv_data->cntl_bits &= ~MV64XXX_I2C_REG_CONTROL_INTEN;
+		writel(drv_data->cntl_bits | MV64XXX_I2C_REG_CONTROL_STOP,
+			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+		drv_data->block = 0;
+		wake_up_interruptible(&drv_data->waitq);
+		break;
+
+	case MV64XXX_I2C_ACTION_INVALID:
+	default:
+		dev_err(&drv_data->adapter.dev,
+			"mv64xxx_i2c_do_action: Invalid action: %d\n",
+			drv_data->action);
+		drv_data->rc = -EIO;
+		/* FALLTHRU */
+	case MV64XXX_I2C_ACTION_SEND_STOP:
+		drv_data->cntl_bits &= ~MV64XXX_I2C_REG_CONTROL_INTEN;
+		writel(drv_data->cntl_bits | MV64XXX_I2C_REG_CONTROL_STOP,
+			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+		drv_data->block = 0;
+		wake_up_interruptible(&drv_data->waitq);
+		break;
+	}
+
+	return;
+}
+
+static int
+mv64xxx_i2c_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct mv64xxx_i2c_data	*drv_data = dev_id;
+	u32	status;
+	long	flags;
+	int	rc = IRQ_NONE;
+
+	spin_lock_irqsave(&drv_data->lock, flags);
+	while (readl(drv_data->reg_base + MV64XXX_I2C_REG_CONTROL) &
+						MV64XXX_I2C_REG_CONTROL_IFLG) {
+		status = readl(drv_data->reg_base + MV64XXX_I2C_REG_STATUS);
+		mv64xxx_i2c_fsm(drv_data, status);
+		mv64xxx_i2c_do_action(drv_data);
+		rc = IRQ_HANDLED;
+	}
+	spin_unlock_irqrestore(&drv_data->lock, flags);
+
+	return rc;
+}
+
+/*
+ *****************************************************************************
+ *
+ *	I2C Msg Execution Routines
+ *
+ *****************************************************************************
+ */
+static void
+mv64xxx_i2c_prepare_for_io(struct mv64xxx_i2c_data *drv_data,
+	struct i2c_msg *msg)
+{
+	u32	dir = 0;
+
+	drv_data->msg = msg;
+	drv_data->byte_posn = 0;
+	drv_data->bytes_left = msg->len;
+	drv_data->rc = 0;
+	drv_data->cntl_bits = MV64XXX_I2C_REG_CONTROL_ACK |
+		MV64XXX_I2C_REG_CONTROL_INTEN | MV64XXX_I2C_REG_CONTROL_TWSIEN;
+
+	if (msg->flags & I2C_M_RD)
+		dir = 1;
+
+	if (msg->flags & I2C_M_REV_DIR_ADDR)
+		dir ^= 1;
+
+	if (msg->flags & I2C_M_TEN) {
+		drv_data->addr1 = 0xf0 | (((u32)msg->addr & 0x300) >> 7) | dir;
+		drv_data->addr2 = (u32)msg->addr & 0xff;
+	}
+	else {
+		drv_data->addr1 = ((u32)msg->addr & 0x7f) << 1 | dir;
+		drv_data->addr2 = 0;
+	}
+
+	return;
+}
+
+static void
+mv64xxx_i2c_wait_for_completion(struct mv64xxx_i2c_data *drv_data)
+{
+	long	flags, time_left;
+	char	abort = 0;
+
+	time_left = wait_event_interruptible_timeout(drv_data->waitq,
+		!drv_data->block, msecs_to_jiffies(drv_data->adapter.timeout));
+
+	spin_lock_irqsave(&drv_data->lock, flags);
+	if (!time_left) { /* Timed out */
+		drv_data->rc = -ETIMEDOUT;
+		abort = 1;
+	}
+	else if (time_left < 0) { /* Interrupted/Error */
+		drv_data->rc = time_left; /* errno value */
+		abort = 1;
+	}
+
+	if (abort && drv_data->block) {
+		drv_data->state = MV64XXX_I2C_STATE_ABORTING;
+		spin_unlock_irqrestore(&drv_data->lock, flags);
+
+		time_left = wait_event_timeout(drv_data->waitq,
+			!drv_data->block,
+			msecs_to_jiffies(drv_data->adapter.timeout));
+
+		if (!time_left <= 0) {
+			drv_data->state = MV64XXX_I2C_STATE_IDLE;
+			dev_err(&drv_data->adapter.dev,
+				"mv64xxx: I2C bus locked\n");
+		}
+	}
+	else
+		spin_unlock_irqrestore(&drv_data->lock, flags);
+
+	return;
+}
+
+static int
+mv64xxx_i2c_execute_msg(struct mv64xxx_i2c_data *drv_data, struct i2c_msg *msg)
+{
+	long	flags;
+
+	spin_lock_irqsave(&drv_data->lock, flags);
+	mv64xxx_i2c_prepare_for_io(drv_data, msg);
+
+	if (unlikely(msg->flags & I2C_M_NOSTART)) { /* Skip start/addr phases */
+		if (drv_data->msg->flags & I2C_M_RD) {
+			/* No action to do, wait for slave to send a byte */
+			drv_data->action = MV64XXX_I2C_ACTION_CONTINUE;
+			drv_data->state =
+				MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_DATA;
+		}
+		else {
+			drv_data->action = MV64XXX_I2C_ACTION_SEND_DATA;
+			drv_data->state =
+				MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_ACK;
+			drv_data->bytes_left--;
+		}
+	}
+	else {
+		drv_data->action = MV64XXX_I2C_ACTION_SEND_START;
+		drv_data->state = MV64XXX_I2C_STATE_WAITING_FOR_START_COND;
+	}
+
+	drv_data->block = 1;
+	mv64xxx_i2c_do_action(drv_data);
+	spin_unlock_irqrestore(&drv_data->lock, flags);
+
+	mv64xxx_i2c_wait_for_completion(drv_data);
+	return drv_data->rc;
+}
+
+/*
+ *****************************************************************************
+ *
+ *	I2C Core Support Routines (Interface to higher level I2C code)
+ *
+ *****************************************************************************
+ */
+static u32
+mv64xxx_i2c_functionality(struct i2c_adapter *adap)
+{
+	return I2C_FUNC_I2C |I2C_FUNC_10BIT_ADDR | I2C_FUNC_SMBUS_EMUL;
+}
+
+static int
+mv64xxx_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
+{
+	struct mv64xxx_i2c_data *drv_data = i2c_get_adapdata(adap);
+	int	i, rc = 0;
+
+	for (i=0; i<num; i++)
+		if ((rc = mv64xxx_i2c_execute_msg(drv_data, &msgs[i])) != 0)
+			break;
+
+	return rc;
+}
+
+static struct i2c_algorithm mv64xxx_i2c_algo = {
+	.name = MV64XXX_I2C_CTLR_NAME "algorithm",
+	.id = I2C_ALGO_MV64XXX,
+	.master_xfer = mv64xxx_i2c_xfer,
+	.functionality = mv64xxx_i2c_functionality,
+};
+
+/*
+ *****************************************************************************
+ *
+ *	Driver Interface & Early Init Routines
+ *
+ *****************************************************************************
+ */
+static void __devinit
+mv64xxx_i2c_hw_init(struct mv64xxx_i2c_data *drv_data)
+{
+	writel(0, drv_data->reg_base + MV64XXX_I2C_REG_SOFT_RESET);
+	writel((((drv_data->freq_m & 0xf) << 3) | (drv_data->freq_n & 0x7)),
+		drv_data->reg_base + MV64XXX_I2C_REG_BAUD);
+	writel(0, drv_data->reg_base + MV64XXX_I2C_REG_SLAVE_ADDR);
+	writel(0, drv_data->reg_base + MV64XXX_I2C_REG_EXT_SLAVE_ADDR);
+	writel(MV64XXX_I2C_REG_CONTROL_TWSIEN | MV64XXX_I2C_REG_CONTROL_STOP,
+		drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+	drv_data->state = MV64XXX_I2C_STATE_IDLE;
+	return;
+}
+
+static int __devinit
+mv64xxx_i2c_map_regs(struct platform_device *pd,
+	struct mv64xxx_i2c_data *drv_data)
+{
+	struct resource	*r;
+
+	if ((r = platform_get_resource(pd, IORESOURCE_MEM, 0)) &&
+		request_mem_region(r->start, MV64XXX_I2C_REG_BLOCK_SIZE,
+			drv_data->adapter.name)) {
+
+		drv_data->reg_base = ioremap(r->start,
+			MV64XXX_I2C_REG_BLOCK_SIZE);
+		drv_data->reg_base_p = r->start;
+	}
+	else
+		return -ENOMEM;
+
+	return 0;
+}
+
+static void __devexit
+mv64xxx_i2c_unmap_regs(struct mv64xxx_i2c_data *drv_data)
+{
+	if (!drv_data->reg_base) {
+		iounmap(drv_data->reg_base);
+		release_mem_region(drv_data->reg_base_p,
+			MV64XXX_I2C_REG_BLOCK_SIZE);
+	}
+
+	drv_data->reg_base = 0;
+	drv_data->reg_base_p = 0;
+	return;
+}
+
+static int __devinit
+mv64xxx_i2c_probe(struct device *dev)
+{
+	struct platform_device		*pd = to_platform_device(dev);
+	struct mv64xxx_i2c_data		*drv_data;
+	struct mv64xxx_i2c_pdata	*pdata = dev->platform_data;
+	int	rc;
+
+	if ((pd->id == 0) && pdata) {
+		drv_data = kmalloc(sizeof(struct mv64xxx_i2c_data), GFP_KERNEL);
+
+		if (!drv_data)
+			return -ENOMEM;
+
+		memset(drv_data, 0, sizeof(struct mv64xxx_i2c_data));
+
+		if (mv64xxx_i2c_map_regs(pd, drv_data)) {
+			kfree(drv_data);
+			return -ENODEV;
+		}
+
+		strncpy(drv_data->adapter.name, MV64XXX_I2C_CTLR_NAME "adapter",
+			I2C_NAME_SIZE);
+
+		init_waitqueue_head(&drv_data->waitq);
+		spin_lock_init(&drv_data->lock);
+
+		drv_data->freq_m = pdata->freq_m;
+		drv_data->freq_n = pdata->freq_n;
+		drv_data->irq = platform_get_irq(pd, 0);
+		drv_data->adapter.id = I2C_ALGO_MV64XXX | I2C_HW_MV64XXX;
+		drv_data->adapter.algo = &mv64xxx_i2c_algo;
+		drv_data->adapter.timeout = pdata->timeout;
+		drv_data->adapter.retries = pdata->retries;
+		dev_set_drvdata(dev, drv_data);
+		i2c_set_adapdata(&drv_data->adapter, drv_data);
+
+		if (request_irq(drv_data->irq, mv64xxx_i2c_intr, 0,
+			MV64XXX_I2C_CTLR_NAME, drv_data)) {
+
+			dev_err(dev, "mv64xxx: Can't register intr handler "
+				"irq: %d\\n", drv_data->irq);
+
+			mv64xxx_i2c_unmap_regs(drv_data);
+			kfree(drv_data);
+			return -EINVAL;
+		}
+		else if ((rc = i2c_add_adapter(&drv_data->adapter)) != 0) {
+			dev_err(dev, "mv64xxx: Can't add i2c adapter, rc: %d\n",
+				-rc);
+			free_irq(drv_data->irq, drv_data);
+			mv64xxx_i2c_unmap_regs(drv_data);
+			kfree(drv_data);
+			return rc;
+		}
+
+		mv64xxx_i2c_hw_init(drv_data);
+	}
+	else
+		return -ENODEV;
+
+	return 0;
+}
+
+static int __devexit
+mv64xxx_i2c_remove(struct device *dev)
+{
+	struct mv64xxx_i2c_data		*drv_data = dev_get_drvdata(dev);
+	int	rc;
+
+	rc = i2c_del_adapter(&drv_data->adapter);
+	free_irq(drv_data->irq, drv_data);
+	mv64xxx_i2c_unmap_regs(drv_data);
+	kfree(drv_data);
+
+	return rc;
+}
+
+static struct device_driver mv64xxx_i2c_driver = {
+	.name	= MV64XXX_I2C_CTLR_NAME,
+	.bus	= &platform_bus_type,
+	.probe	= mv64xxx_i2c_probe,
+	.remove	= mv64xxx_i2c_remove,
+};
+
+static int __devinit
+mv64xxx_i2c_init(void)
+{
+	return driver_register(&mv64xxx_i2c_driver);
+}
+
+static void __devexit
+mv64xxx_i2c_exit(void)
+{
+	driver_unregister(&mv64xxx_i2c_driver);
+	return;
+}
+
+module_init(mv64xxx_i2c_init);
+module_exit(mv64xxx_i2c_exit);
+
+MODULE_AUTHOR("Mark A. Greer <mgreer@mvista.com>");
+MODULE_DESCRIPTION("Marvell mv64xxx host bridge i2c ctlr driver");
+MODULE_LICENSE("GPL");
diff -Nru a/include/linux/i2c-id.h b/include/linux/i2c-id.h
--- a/include/linux/i2c-id.h	2005-02-01 10:45:00 -07:00
+++ b/include/linux/i2c-id.h	2005-02-01 10:45:00 -07:00
@@ -200,6 +200,9 @@
 
 #define I2C_ALGO_SIBYTE 0x150000	/* Broadcom SiByte SOCs		*/
 #define I2C_ALGO_SGI	0x160000        /* SGI algorithm                */
+					/* 0x170000 - USB		*/
+					/* 0x180000 - Virtual buses	*/
+#define I2C_ALGO_MV64XXX 0x190000       /* Marvell mv64xxx i2c ctlr	*/
 
 #define I2C_ALGO_EXP	0x800000	/* experimental			*/
 
@@ -305,5 +308,8 @@
 
 /* --- MCP107 adapter */
 #define I2C_HW_MPC107 0x00
+
+/* --- Marvell mv64xxx i2c adapter */
+#define I2C_HW_MV64XXX 0x00
 
 #endif /* LINUX_I2C_ID_H */
diff -Nru a/include/linux/mv643xx.h b/include/linux/mv643xx.h
--- a/include/linux/mv643xx.h	2005-02-01 10:45:00 -07:00
+++ b/include/linux/mv643xx.h	2005-02-01 10:45:00 -07:00
@@ -977,12 +977,9 @@
 /* I2C Registers                        */
 /****************************************/
 
-#define MV64340_I2C_SLAVE_ADDR                                      0xc000
-#define MV64340_I2C_EXTENDED_SLAVE_ADDR                             0xc010
-#define MV64340_I2C_DATA                                            0xc004
-#define MV64340_I2C_CONTROL                                         0xc008
-#define MV64340_I2C_STATUS_BAUDE_RATE                               0xc00C
-#define MV64340_I2C_SOFT_RESET                                      0xc01c
+#define MV64XXX_I2C_CTLR_NAME					"mv64xxx i2c"
+#define MV64XXX_I2C_OFFSET                                          0xc000
+#define MV64XXX_I2C_REG_BLOCK_SIZE                                  0x0020
 
 /****************************************/
 /* GPP Interface Registers              */
@@ -1083,6 +1080,14 @@
 	u8	brg_can_tune;
 	u8	brg_clk_src;
 	u32	brg_clk_freq;
+};
+
+/* i2c Platform Device, Driver Data */
+struct mv64xxx_i2c_pdata {
+	u32	freq_m;
+	u32	freq_n;
+	u32	timeout;	/* In milliseconds */
+	u32	retries;
 };
 
 #endif /* __ASM_MV64340_H */

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

* Re: [PATCH][I2C] Marvell mv64xxx i2c driver
  2005-01-31 18:41   ` Mark A. Greer
@ 2005-02-01  0:46     ` Greg KH
  2005-02-01 17:54       ` Mark A. Greer
  0 siblings, 1 reply; 23+ messages in thread
From: Greg KH @ 2005-02-01  0:46 UTC (permalink / raw)
  To: Mark A. Greer; +Cc: phil, sensors, lkml

On Mon, Jan 31, 2005 at 11:41:28AM -0700, Mark A. Greer wrote:
> Greg KH wrote:
> 
> >On Tue, Jan 25, 2005 at 06:26:45PM -0700, Mark A. Greer wrote:
> > 
> >
> >>+static inline void
> >>+mv64xxx_i2c_fsm(struct mv64xxx_i2c_data *drv_data, u32 status)
> >>   
> >>
> >
> >This is a much too big of a function to be "inline".  Please change it.
> >Same for your other inline functions, that's not really needed, right?
> >
> > 
> >
> >>+{
> >>+	pr_debug("mv64xxx_i2c_fsm: ENTER--state: %d, status: 0x%x\n",
> >>+		drv_data->state, status);
> >>   
> >>
> >
> >Please use the dev_* calls instead.  It gives you an accurate
> >description of the specific device that emits the messages.  Also use it
> >for all of the printk() calls in the driver too.
> >
> >thanks,
> >
> >greg k-h
> >
> 
> Certainly.  I already posted this [incremental] patch based on your 
> previous comments, 
> http://www.ussg.iu.edu/hypermail/linux/kernel/0501.3/0941.html.  Is that 
> better?

How about a whole new patch that I could apply?  That would be better :)

thanks,

greg k-h

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

* Re: [PATCH][I2C] Marvell mv64xxx i2c driver
  2005-01-31 18:25 ` Greg KH
@ 2005-01-31 18:41   ` Mark A. Greer
  2005-02-01  0:46     ` Greg KH
  0 siblings, 1 reply; 23+ messages in thread
From: Mark A. Greer @ 2005-01-31 18:41 UTC (permalink / raw)
  To: Greg KH; +Cc: phil, sensors, lkml

Greg KH wrote:

>On Tue, Jan 25, 2005 at 06:26:45PM -0700, Mark A. Greer wrote:
>  
>
>>+static inline void
>>+mv64xxx_i2c_fsm(struct mv64xxx_i2c_data *drv_data, u32 status)
>>    
>>
>
>This is a much too big of a function to be "inline".  Please change it.
>Same for your other inline functions, that's not really needed, right?
>
>  
>
>>+{
>>+	pr_debug("mv64xxx_i2c_fsm: ENTER--state: %d, status: 0x%x\n",
>>+		drv_data->state, status);
>>    
>>
>
>Please use the dev_* calls instead.  It gives you an accurate
>description of the specific device that emits the messages.  Also use it
>for all of the printk() calls in the driver too.
>
>thanks,
>
>greg k-h
>

Certainly.  I already posted this [incremental] patch based on your 
previous comments, 
http://www.ussg.iu.edu/hypermail/linux/kernel/0501.3/0941.html.  Is that 
better?

Mark


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

* Re: [PATCH][I2C] Marvell mv64xxx i2c driver
  2005-01-26  1:26 Mark A. Greer
  2005-01-26 19:56 ` Jean Delvare
@ 2005-01-31 18:25 ` Greg KH
  2005-01-31 18:41   ` Mark A. Greer
  1 sibling, 1 reply; 23+ messages in thread
From: Greg KH @ 2005-01-31 18:25 UTC (permalink / raw)
  To: Mark A. Greer; +Cc: phil, sensors, lkml

On Tue, Jan 25, 2005 at 06:26:45PM -0700, Mark A. Greer wrote:
> +static inline void
> +mv64xxx_i2c_fsm(struct mv64xxx_i2c_data *drv_data, u32 status)

This is a much too big of a function to be "inline".  Please change it.
Same for your other inline functions, that's not really needed, right?

> +{
> +	pr_debug("mv64xxx_i2c_fsm: ENTER--state: %d, status: 0x%x\n",
> +		drv_data->state, status);

Please use the dev_* calls instead.  It gives you an accurate
description of the specific device that emits the messages.  Also use it
for all of the printk() calls in the driver too.

thanks,

greg k-h

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

* Re: [PATCH][I2C] Marvell mv64xxx i2c driver
  2005-01-26 22:42     ` Greg KH
@ 2005-01-26 23:59       ` Mark A. Greer
  0 siblings, 0 replies; 23+ messages in thread
From: Mark A. Greer @ 2005-01-26 23:59 UTC (permalink / raw)
  To: Greg KH; +Cc: LM Sensors, LKML

[-- Attachment #1: Type: text/plain, Size: 767 bytes --]

Greg KH wrote:

>Please put <asm/ after <linux/
>

Done.

>You have a lot of pr_debug and other printk() for stuff in this driver.
>Please use dev_dbg(), dev_err() and friends instead.  That way you get a
>consistant message, that points to the exact device that is causing the
>message.
>

Cool.  Done.

>You have some big inline functions here.  Should they really be inline?
>We aren't really worried about speed here, right?  Size is probably a
>bigger issue.
>

No, no, and done.

>
>Is this header file really needed?  Does any other file other than this
>single driver ever include it?  If not, please just put it into the
>driver itself.
>  
>

No, no, and done.

Included is an *incremental* patch that I hope addresses your concerns.

Thanks Greg.

Mark
--

[-- Attachment #2: i2c_3.patch --]
[-- Type: text/plain, Size: 10496 bytes --]

diff -Nru a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c
--- a/drivers/i2c/busses/i2c-mv64xxx.c	2005-01-26 16:52:56 -07:00
+++ b/drivers/i2c/busses/i2c-mv64xxx.c	2005-01-26 16:52:56 -07:00
@@ -11,21 +11,94 @@
  * is licensed "as is" without any warranty of any kind, whether express
  * or implied.
  */
-#include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
-#include <linux/sched.h>
-#include <linux/init.h>
-#include <linux/pci.h>
-#include <linux/wait.h>
 #include <linux/spinlock.h>
-#include <asm/io.h>
-#include <asm/ocp.h>
 #include <linux/i2c.h>
 #include <linux/interrupt.h>
-#include <linux/delay.h>
 #include <linux/mv643xx.h>
-#include "i2c-mv64xxx.h"
+#include <asm/io.h>
+
+/* Register defines */
+#define	MV64XXX_I2C_REG_SLAVE_ADDR			0x00
+#define	MV64XXX_I2C_REG_DATA				0x04
+#define	MV64XXX_I2C_REG_CONTROL				0x08
+#define	MV64XXX_I2C_REG_STATUS				0x0c
+#define	MV64XXX_I2C_REG_BAUD				0x0c
+#define	MV64XXX_I2C_REG_EXT_SLAVE_ADDR			0x10
+#define	MV64XXX_I2C_REG_SOFT_RESET			0x1c
+
+#define	MV64XXX_I2C_REG_CONTROL_ACK			0x00000004
+#define	MV64XXX_I2C_REG_CONTROL_IFLG			0x00000008
+#define	MV64XXX_I2C_REG_CONTROL_STOP			0x00000010
+#define	MV64XXX_I2C_REG_CONTROL_START			0x00000020
+#define	MV64XXX_I2C_REG_CONTROL_TWSIEN			0x00000040
+#define	MV64XXX_I2C_REG_CONTROL_INTEN			0x00000080
+
+/* Ctlr status values */
+#define	MV64XXX_I2C_STATUS_BUS_ERR			0x00
+#define	MV64XXX_I2C_STATUS_MAST_START			0x08
+#define	MV64XXX_I2C_STATUS_MAST_REPEAT_START		0x10
+#define	MV64XXX_I2C_STATUS_MAST_WR_ADDR_ACK		0x18
+#define	MV64XXX_I2C_STATUS_MAST_WR_ADDR_NO_ACK		0x20
+#define	MV64XXX_I2C_STATUS_MAST_WR_ACK			0x28
+#define	MV64XXX_I2C_STATUS_MAST_WR_NO_ACK		0x30
+#define	MV64XXX_I2C_STATUS_MAST_LOST_ARB		0x38
+#define	MV64XXX_I2C_STATUS_MAST_RD_ADDR_ACK		0x40
+#define	MV64XXX_I2C_STATUS_MAST_RD_ADDR_NO_ACK		0x48
+#define	MV64XXX_I2C_STATUS_MAST_RD_DATA_ACK		0x50
+#define	MV64XXX_I2C_STATUS_MAST_RD_DATA_NO_ACK		0x58
+#define	MV64XXX_I2C_STATUS_MAST_WR_ADDR_2_ACK		0xd0
+#define	MV64XXX_I2C_STATUS_MAST_WR_ADDR_2_NO_ACK	0xd8
+#define	MV64XXX_I2C_STATUS_MAST_RD_ADDR_2_ACK		0xe0
+#define	MV64XXX_I2C_STATUS_MAST_RD_ADDR_2_NO_ACK	0xe8
+#define	MV64XXX_I2C_STATUS_NO_STATUS			0xf8
+
+/* Driver states */
+enum {
+	MV64XXX_I2C_STATE_INVALID,
+	MV64XXX_I2C_STATE_IDLE,
+	MV64XXX_I2C_STATE_WAITING_FOR_START_COND,
+	MV64XXX_I2C_STATE_WAITING_FOR_ADDR_1_ACK,
+	MV64XXX_I2C_STATE_WAITING_FOR_ADDR_2_ACK,
+	MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_ACK,
+	MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_DATA,
+	MV64XXX_I2C_STATE_ABORTING,
+};
+
+/* Driver actions */
+enum {
+	MV64XXX_I2C_ACTION_INVALID,
+	MV64XXX_I2C_ACTION_CONTINUE,
+	MV64XXX_I2C_ACTION_SEND_START,
+	MV64XXX_I2C_ACTION_SEND_ADDR_1,
+	MV64XXX_I2C_ACTION_SEND_ADDR_2,
+	MV64XXX_I2C_ACTION_SEND_DATA,
+	MV64XXX_I2C_ACTION_RCV_DATA,
+	MV64XXX_I2C_ACTION_RCV_DATA_STOP,
+	MV64XXX_I2C_ACTION_SEND_STOP,
+};
+
+struct mv64xxx_i2c_data {
+	int			irq;
+	uint			state;
+	uint			action;
+	u32			cntl_bits;
+	void			*reg_base;
+	ulong			reg_base_p;
+	u32			addr1;
+	u32			addr2;
+	uint			bytes_left;
+	uint			byte_posn;
+	uint			block;
+	int			rc;
+	u32			freq_m;
+	u32			freq_n;
+	wait_queue_head_t	waitq;
+	spinlock_t		lock;
+	struct i2c_msg		*msg;
+	struct i2c_adapter	adapter;
+};
 
 /*
  *****************************************************************************
@@ -34,12 +107,9 @@
  *
  *****************************************************************************
  */
-static inline void
+static void
 mv64xxx_i2c_fsm(struct mv64xxx_i2c_data *drv_data, u32 status)
 {
-	pr_debug("mv64xxx_i2c_fsm: ENTER--state: %d, status: 0x%x\n",
-		drv_data->state, status);
-
 	/*
 	 * If state is idle, then this is likely the remnants of an old
 	 * operation that driver has given up on or the user has killed.
@@ -48,14 +118,12 @@
 	if (drv_data->state == MV64XXX_I2C_STATE_IDLE) {
 		drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
 		drv_data->state = MV64XXX_I2C_STATE_IDLE;
-		pr_debug("mv64xxx_i2c_fsm: EXIT--Entered when in IDLE state\n");
 		return;
 	}
 
 	if (drv_data->state == MV64XXX_I2C_STATE_ABORTING) {
 		drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
 		drv_data->state = MV64XXX_I2C_STATE_IDLE;
-		pr_debug("mv64xxx_i2c_fsm: EXIT--Aborting\n");
 		return;
 	}
 
@@ -135,27 +203,22 @@
 		break;
 
 	default:
-		printk(KERN_ERR "mv64xxx_i2c_fsm: Ctlr Error -- "
-			"state: 0x%x, status: 0x%x\n", drv_data->state, status);
-		printk(KERN_INFO "addr: 0x%x, flags: 0x%x\n",
-			drv_data->msg->addr, drv_data->msg->flags);
+		dev_err(&drv_data->adapter.dev,
+			"mv64xxx_i2c_fsm: Ctlr Error -- state: 0x%x, "
+			"status: 0x%x, addr: 0x%x, flags: 0x%x\n",
+			 drv_data->state, status, drv_data->msg->addr,
+			 drv_data->msg->flags);
 		drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
 		drv_data->state = MV64XXX_I2C_STATE_IDLE;
 		drv_data->rc = -EIO;
 	}
 
-	pr_debug("mv64xxx_i2c_fsm: EXIT--action: %d, state: %d, rc: 0x%x\n",
-		drv_data->action, drv_data->state, drv_data->rc);
 	return;
 }
 
 static void
 mv64xxx_i2c_do_action(struct mv64xxx_i2c_data *drv_data)
 {
-	pr_debug("mv64xxx_i2c_do_action: ENTER--action: %d, state: %d, "
-		"cntl: 0x%x\n", drv_data->action, drv_data->state,
-		drv_data->cntl_bits);
-
 	switch(drv_data->action) {
 	case MV64XXX_I2C_ACTION_CONTINUE:
 		writel(drv_data->cntl_bits,
@@ -207,7 +270,8 @@
 
 	case MV64XXX_I2C_ACTION_INVALID:
 	default:
-		printk(KERN_ERR "mv64xxx_i2c_do_action: Invalid action: %d\n",
+		dev_err(&drv_data->adapter.dev,
+			"mv64xxx_i2c_do_action: Invalid action: %d\n",
 			drv_data->action);
 		drv_data->rc = -EIO;
 		/* FALLTHRU */
@@ -220,7 +284,6 @@
 		break;
 	}
 
-	pr_debug("mv64xxx_i2c_do_action: EXIT\n");
 	return;
 }
 
@@ -252,7 +315,7 @@
  *
  *****************************************************************************
  */
-static inline void
+static void
 mv64xxx_i2c_prepare_for_io(struct mv64xxx_i2c_data *drv_data,
 	struct i2c_msg *msg)
 {
@@ -283,7 +346,7 @@
 	return;
 }
 
-static inline void
+static void
 mv64xxx_i2c_wait_for_completion(struct mv64xxx_i2c_data *drv_data)
 {
 	long	flags, time_left;
@@ -312,7 +375,8 @@
 
 		if (!time_left <= 0) {
 			drv_data->state = MV64XXX_I2C_STATE_IDLE;
-			printk(KERN_WARNING "mv64xxx: I2C bus locked\n");
+			dev_err(&drv_data->adapter.dev,
+				"mv64xxx: I2C bus locked\n");
 		}
 	}
 	else
@@ -321,7 +385,7 @@
 	return;
 }
 
-static inline int
+static int
 mv64xxx_i2c_execute_msg(struct mv64xxx_i2c_data *drv_data, struct i2c_msg *msg)
 {
 	long	flags;
@@ -484,7 +548,7 @@
 		if (request_irq(drv_data->irq, mv64xxx_i2c_intr, 0,
 			MV64XXX_I2C_CTLR_NAME, drv_data)) {
 
-			printk(KERN_ERR "mv64xxx: Can't register intr handler "
+			dev_err(dev, "mv64xxx: Can't register intr handler "
 				"irq: %d\\n", drv_data->irq);
 
 			mv64xxx_i2c_unmap_regs(drv_data);
@@ -492,8 +556,8 @@
 			return -EINVAL;
 		}
 		else if ((rc = i2c_add_adapter(&drv_data->adapter)) != 0) {
-			printk(KERN_WARNING "mv64xxx: Can't add i2c adapter "
-				"rc: %d\n", -rc);
+			dev_err(dev, "mv64xxx: Can't add i2c adapter, rc: %d\n",
+				-rc);
 			free_irq(drv_data->irq, drv_data);
 			mv64xxx_i2c_unmap_regs(drv_data);
 			kfree(drv_data);
diff -Nru a/drivers/i2c/busses/i2c-mv64xxx.h b/drivers/i2c/busses/i2c-mv64xxx.h
--- a/drivers/i2c/busses/i2c-mv64xxx.h	2005-01-26 16:52:56 -07:00
+++ /dev/null	Wed Dec 31 16:00:00 196900
@@ -1,99 +0,0 @@
-/*
- * drivers/i2c/busses/i2c-mv64xxx.h
- * 
- * Driver for the i2c controller on the Marvell line of host bridges for MIPS
- * and PPC (e.g, gt642[46]0, mv643[46]0, mv644[46]0).
- *
- * Author: Mark A. Greer <mgreer@mvista.com>
- *
- * 2005 (c) MontaVista, Software, Inc.  This file is licensed under
- * the terms of the GNU General Public License version 2.  This program
- * is licensed "as is" without any warranty of any kind, whether express
- * or implied.
- */
-
-#ifndef I2C_MV64XXX_H
-#define I2C_MV64XXX_H
-
-/* Register defines */
-#define	MV64XXX_I2C_REG_SLAVE_ADDR			0x00
-#define	MV64XXX_I2C_REG_DATA				0x04
-#define	MV64XXX_I2C_REG_CONTROL				0x08
-#define	MV64XXX_I2C_REG_STATUS				0x0c
-#define	MV64XXX_I2C_REG_BAUD				0x0c
-#define	MV64XXX_I2C_REG_EXT_SLAVE_ADDR			0x10
-#define	MV64XXX_I2C_REG_SOFT_RESET			0x1c
-
-#define	MV64XXX_I2C_REG_CONTROL_ACK			0x00000004
-#define	MV64XXX_I2C_REG_CONTROL_IFLG			0x00000008
-#define	MV64XXX_I2C_REG_CONTROL_STOP			0x00000010
-#define	MV64XXX_I2C_REG_CONTROL_START			0x00000020
-#define	MV64XXX_I2C_REG_CONTROL_TWSIEN			0x00000040
-#define	MV64XXX_I2C_REG_CONTROL_INTEN			0x00000080
-
-/* Ctlr status values */
-#define	MV64XXX_I2C_STATUS_BUS_ERR			0x00
-#define	MV64XXX_I2C_STATUS_MAST_START			0x08
-#define	MV64XXX_I2C_STATUS_MAST_REPEAT_START		0x10
-#define	MV64XXX_I2C_STATUS_MAST_WR_ADDR_ACK		0x18
-#define	MV64XXX_I2C_STATUS_MAST_WR_ADDR_NO_ACK		0x20
-#define	MV64XXX_I2C_STATUS_MAST_WR_ACK			0x28
-#define	MV64XXX_I2C_STATUS_MAST_WR_NO_ACK		0x30
-#define	MV64XXX_I2C_STATUS_MAST_LOST_ARB		0x38
-#define	MV64XXX_I2C_STATUS_MAST_RD_ADDR_ACK		0x40
-#define	MV64XXX_I2C_STATUS_MAST_RD_ADDR_NO_ACK		0x48
-#define	MV64XXX_I2C_STATUS_MAST_RD_DATA_ACK		0x50
-#define	MV64XXX_I2C_STATUS_MAST_RD_DATA_NO_ACK		0x58
-#define	MV64XXX_I2C_STATUS_MAST_WR_ADDR_2_ACK		0xd0
-#define	MV64XXX_I2C_STATUS_MAST_WR_ADDR_2_NO_ACK	0xd8
-#define	MV64XXX_I2C_STATUS_MAST_RD_ADDR_2_ACK		0xe0
-#define	MV64XXX_I2C_STATUS_MAST_RD_ADDR_2_NO_ACK	0xe8
-#define	MV64XXX_I2C_STATUS_NO_STATUS			0xf8
-
-/* Driver states */
-enum {
-	MV64XXX_I2C_STATE_INVALID,
-	MV64XXX_I2C_STATE_IDLE,
-	MV64XXX_I2C_STATE_WAITING_FOR_START_COND,
-	MV64XXX_I2C_STATE_WAITING_FOR_ADDR_1_ACK,
-	MV64XXX_I2C_STATE_WAITING_FOR_ADDR_2_ACK,
-	MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_ACK,
-	MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_DATA,
-	MV64XXX_I2C_STATE_ABORTING,
-};
-
-/* Driver actions */
-enum {
-	MV64XXX_I2C_ACTION_INVALID,
-	MV64XXX_I2C_ACTION_CONTINUE,
-	MV64XXX_I2C_ACTION_SEND_START,
-	MV64XXX_I2C_ACTION_SEND_ADDR_1,
-	MV64XXX_I2C_ACTION_SEND_ADDR_2,
-	MV64XXX_I2C_ACTION_SEND_DATA,
-	MV64XXX_I2C_ACTION_RCV_DATA,
-	MV64XXX_I2C_ACTION_RCV_DATA_STOP,
-	MV64XXX_I2C_ACTION_SEND_STOP,
-};
-
-struct mv64xxx_i2c_data {
-	int			irq;
-	uint			state;
-	uint			action;
-	u32			cntl_bits;
-	void			*reg_base;
-	ulong			reg_base_p;
-	u32			addr1;
-	u32			addr2;
-	uint			bytes_left;
-	uint			byte_posn;
-	uint			block;
-	int			rc;
-	u32			freq_m;
-	u32			freq_n;
-	wait_queue_head_t	waitq;
-	spinlock_t		lock;
-	struct i2c_msg		*msg;
-	struct i2c_adapter	adapter;
-};
-
-#endif /* I2C_MV64XXX_H */

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

* Re: [PATCH][I2C] Marvell mv64xxx i2c driver
  2005-01-26 21:56   ` Mark A. Greer
@ 2005-01-26 22:42     ` Greg KH
  2005-01-26 23:59       ` Mark A. Greer
  0 siblings, 1 reply; 23+ messages in thread
From: Greg KH @ 2005-01-26 22:42 UTC (permalink / raw)
  To: Mark A. Greer; +Cc: LM Sensors, LKML

On Wed, Jan 26, 2005 at 02:56:55PM -0700, Mark A. Greer wrote:
> +#include <linux/config.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/sched.h>
> +#include <linux/init.h>
> +#include <linux/pci.h>
> +#include <linux/wait.h>
> +#include <linux/spinlock.h>
> +#include <asm/io.h>
> +#include <asm/ocp.h>
> +#include <linux/i2c.h>
> +#include <linux/interrupt.h>
> +#include <linux/delay.h>
> +#include <linux/mv643xx.h>
> +#include "i2c-mv64xxx.h"

Please put <asm/ after <linux/

> +static inline void
> +mv64xxx_i2c_fsm(struct mv64xxx_i2c_data *drv_data, u32 status)
> +{
> +	pr_debug("mv64xxx_i2c_fsm: ENTER--state: %d, status: 0x%x\n",
> +		drv_data->state, status);

You have a lot of pr_debug and other printk() for stuff in this driver.
Please use dev_dbg(), dev_err() and friends instead.  That way you get a
consistant message, that points to the exact device that is causing the
message.

> +static inline void
> +mv64xxx_i2c_prepare_for_io(struct mv64xxx_i2c_data *drv_data,
> +	struct i2c_msg *msg)

You have some big inline functions here.  Should they really be inline?
We aren't really worried about speed here, right?  Size is probably a
bigger issue.

> diff -Nru a/drivers/i2c/busses/i2c-mv64xxx.h b/drivers/i2c/busses/i2c-mv64xxx.h
> --- /dev/null	Wed Dec 31 16:00:00 196900
> +++ b/drivers/i2c/busses/i2c-mv64xxx.h	2005-01-26 14:49:22 -07:00

Is this header file really needed?  Does any other file other than this
single driver ever include it?  If not, please just put it into the
driver itself.

thanks,

greg k-h

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

* Re: [PATCH][I2C] Marvell mv64xxx i2c driver
  2005-01-26 19:56 ` Jean Delvare
  2005-01-26 20:33   ` Mark A. Greer
@ 2005-01-26 21:56   ` Mark A. Greer
  2005-01-26 22:42     ` Greg KH
  1 sibling, 1 reply; 23+ messages in thread
From: Mark A. Greer @ 2005-01-26 21:56 UTC (permalink / raw)
  To: LM Sensors, LKML; +Cc: Greg KH

[-- Attachment #1: Type: text/plain, Size: 1016 bytes --]

Jean Delvare wrote:

>>+config I2C_MV64XXX
>>+	tristate "Marvell mv64xxx I2C Controller"
>>+	depends on I2C && MV64X60
>>    
>>
>
>&& EXPERIMENTAL?
>

Done.

>>+#define I2C_ALGO_MV64XXX 0x170000       /* Marvell mv64xxx i2c ctlr     */
>>    
>>
>
>0x170000 is reserved within the legacy i2c project for an USB algorithm,
>and 0x180000 for virtual busses. Could you please use 0x190000 instead,
>so as to avoid future collisions?
>

Done.

>
>+#define	MV64XXX_I2C_CTLR_NAME					"mv64xxx i2c"
>+#define MV64XXX_I2C_OFFSET					    0xc000
>+#define MV64XXX_I2C_REG_BLOCK_SIZE				    0x0020
>  
>
>
>You have a tab instead of space before MV64XXX_I2C_CTLR_NAME, it seems.
>Also, you want to align the numerical values using only tabs, no space.
>  
>

Actually, I did use tabs but what was already there used spaces.  So the 
patch won't mess up the alignment, I changed mine to spaces too.

The attached patch is a complete replacement for the one posted earlier.

Signed-off-by: Mark A. Greer <mgreer@mvista.com>
-- 

[-- Attachment #2: i2c_2.patch --]
[-- Type: text/plain, Size: 22462 bytes --]

diff -Nru a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
--- a/drivers/i2c/busses/Kconfig	2005-01-26 14:49:22 -07:00
+++ b/drivers/i2c/busses/Kconfig	2005-01-26 14:49:22 -07:00
@@ -476,4 +476,14 @@
 	  This driver can also be built as a module.  If so, the module
 	  will be called i2c-pca-isa.
 
+config I2C_MV64XXX
+	tristate "Marvell mv64xxx I2C Controller"
+	depends on I2C && MV64X60 && EXPERIMENTAL
+	help
+	  If you say yes to this option, support will be included for the
+	  built-in I2C interface on the Marvell 64xxx line of host bridges
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called i2c-mv64xxx.
+
 endmenu
diff -Nru a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
--- a/drivers/i2c/busses/Makefile	2005-01-26 14:49:22 -07:00
+++ b/drivers/i2c/busses/Makefile	2005-01-26 14:49:22 -07:00
@@ -20,6 +20,7 @@
 obj-$(CONFIG_I2C_IXP4XX)	+= i2c-ixp4xx.o
 obj-$(CONFIG_I2C_KEYWEST)	+= i2c-keywest.o
 obj-$(CONFIG_I2C_MPC)		+= i2c-mpc.o
+obj-$(CONFIG_I2C_MV64XXX)	+= i2c-mv64xxx.o
 obj-$(CONFIG_I2C_NFORCE2)	+= i2c-nforce2.o
 obj-$(CONFIG_I2C_PARPORT)	+= i2c-parport.o
 obj-$(CONFIG_I2C_PARPORT_LIGHT)	+= i2c-parport-light.o
diff -Nru a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/drivers/i2c/busses/i2c-mv64xxx.c	2005-01-26 14:49:22 -07:00
@@ -0,0 +1,550 @@
+/*
+ * drivers/i2c/busses/i2c-mv64xxx.c
+ * 
+ * Driver for the i2c controller on the Marvell line of host bridges for MIPS
+ * and PPC (e.g, gt642[46]0, mv643[46]0, mv644[46]0).
+ *
+ * Author: Mark A. Greer <mgreer@mvista.com>
+ *
+ * 2005 (c) MontaVista, Software, Inc.  This file is licensed under
+ * the terms of the GNU General Public License version 2.  This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/wait.h>
+#include <linux/spinlock.h>
+#include <asm/io.h>
+#include <asm/ocp.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/mv643xx.h>
+#include "i2c-mv64xxx.h"
+
+/*
+ *****************************************************************************
+ *
+ *	Finite State Machine & Interrupt Routines
+ *
+ *****************************************************************************
+ */
+static inline void
+mv64xxx_i2c_fsm(struct mv64xxx_i2c_data *drv_data, u32 status)
+{
+	pr_debug("mv64xxx_i2c_fsm: ENTER--state: %d, status: 0x%x\n",
+		drv_data->state, status);
+
+	/*
+	 * If state is idle, then this is likely the remnants of an old
+	 * operation that driver has given up on or the user has killed.
+	 * If so, issue the stop condition and go to idle.
+	 */
+	if (drv_data->state == MV64XXX_I2C_STATE_IDLE) {
+		drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
+		drv_data->state = MV64XXX_I2C_STATE_IDLE;
+		pr_debug("mv64xxx_i2c_fsm: EXIT--Entered when in IDLE state\n");
+		return;
+	}
+
+	if (drv_data->state == MV64XXX_I2C_STATE_ABORTING) {
+		drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
+		drv_data->state = MV64XXX_I2C_STATE_IDLE;
+		pr_debug("mv64xxx_i2c_fsm: EXIT--Aborting\n");
+		return;
+	}
+
+	/* The status from the ctlr [mostly] tells us what to do next */
+	switch (status) {
+	/* Start condition interrupt */
+	case MV64XXX_I2C_STATUS_MAST_START: /* 0x08 */
+	case MV64XXX_I2C_STATUS_MAST_REPEAT_START: /* 0x10 */
+		drv_data->action = MV64XXX_I2C_ACTION_SEND_ADDR_1;
+		drv_data->state = MV64XXX_I2C_STATE_WAITING_FOR_ADDR_1_ACK;
+		break;
+
+	/* Performing a write */
+	case MV64XXX_I2C_STATUS_MAST_WR_ADDR_ACK: /* 0x18 */
+		if (drv_data->msg->flags & I2C_M_TEN) {
+			drv_data->action = MV64XXX_I2C_ACTION_SEND_ADDR_2;
+			drv_data->state =
+				MV64XXX_I2C_STATE_WAITING_FOR_ADDR_2_ACK;
+			break;
+		}
+		/* FALLTHRU */
+	case MV64XXX_I2C_STATUS_MAST_WR_ADDR_2_ACK: /* 0xd0 */
+	case MV64XXX_I2C_STATUS_MAST_WR_ACK: /* 0x28 */
+		if (drv_data->bytes_left > 0) {
+			drv_data->action = MV64XXX_I2C_ACTION_SEND_DATA;
+			drv_data->state =
+				MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_ACK;
+			drv_data->bytes_left--;
+		}
+		else {
+			drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
+			drv_data->state = MV64XXX_I2C_STATE_IDLE;
+		}
+		break;
+
+	/* Performing a read */
+	case MV64XXX_I2C_STATUS_MAST_RD_ADDR_ACK: /* 40 */
+		if (drv_data->msg->flags & I2C_M_TEN) {
+			drv_data->action = MV64XXX_I2C_ACTION_SEND_ADDR_2;
+			drv_data->state =
+				MV64XXX_I2C_STATE_WAITING_FOR_ADDR_2_ACK;
+			break;
+		}
+		/* FALLTHRU */
+	case MV64XXX_I2C_STATUS_MAST_RD_ADDR_2_ACK: /* 0xe0 */
+		if (drv_data->bytes_left == 0) {
+			drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
+			drv_data->state = MV64XXX_I2C_STATE_IDLE;
+			break;
+		}
+		/* FALLTHRU */
+	case MV64XXX_I2C_STATUS_MAST_RD_DATA_ACK: /* 0x50 */
+		if (status != MV64XXX_I2C_STATUS_MAST_RD_DATA_ACK)
+			drv_data->action = MV64XXX_I2C_ACTION_CONTINUE;
+		else {
+			drv_data->action = MV64XXX_I2C_ACTION_RCV_DATA;
+			drv_data->bytes_left--;
+		}
+		drv_data->state = MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_DATA;
+
+		if (drv_data->bytes_left == 1)
+			drv_data->cntl_bits &= ~MV64XXX_I2C_REG_CONTROL_ACK;
+		break;
+
+	case MV64XXX_I2C_STATUS_MAST_RD_DATA_NO_ACK: /* 0x58 */
+		drv_data->action = MV64XXX_I2C_ACTION_RCV_DATA_STOP;
+		drv_data->state = MV64XXX_I2C_STATE_IDLE;
+		break;
+
+	case MV64XXX_I2C_STATUS_MAST_WR_ADDR_NO_ACK: /* 0x20 */
+	case MV64XXX_I2C_STATUS_MAST_WR_NO_ACK: /* 30 */
+	case MV64XXX_I2C_STATUS_MAST_RD_ADDR_NO_ACK: /* 48 */
+		/* Doesn't seem to be a device at other end */
+		drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
+		drv_data->state = MV64XXX_I2C_STATE_IDLE;
+		drv_data->rc = -ENODEV;
+		break;
+
+	default:
+		printk(KERN_ERR "mv64xxx_i2c_fsm: Ctlr Error -- "
+			"state: 0x%x, status: 0x%x\n", drv_data->state, status);
+		printk(KERN_INFO "addr: 0x%x, flags: 0x%x\n",
+			drv_data->msg->addr, drv_data->msg->flags);
+		drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
+		drv_data->state = MV64XXX_I2C_STATE_IDLE;
+		drv_data->rc = -EIO;
+	}
+
+	pr_debug("mv64xxx_i2c_fsm: EXIT--action: %d, state: %d, rc: 0x%x\n",
+		drv_data->action, drv_data->state, drv_data->rc);
+	return;
+}
+
+static void
+mv64xxx_i2c_do_action(struct mv64xxx_i2c_data *drv_data)
+{
+	pr_debug("mv64xxx_i2c_do_action: ENTER--action: %d, state: %d, "
+		"cntl: 0x%x\n", drv_data->action, drv_data->state,
+		drv_data->cntl_bits);
+
+	switch(drv_data->action) {
+	case MV64XXX_I2C_ACTION_CONTINUE:
+		writel(drv_data->cntl_bits,
+			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+		break;
+
+	case MV64XXX_I2C_ACTION_SEND_START:
+		writel(drv_data->cntl_bits | MV64XXX_I2C_REG_CONTROL_START,
+			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+		break;
+
+	case MV64XXX_I2C_ACTION_SEND_ADDR_1:
+		writel(drv_data->addr1,
+			drv_data->reg_base + MV64XXX_I2C_REG_DATA);
+		writel(drv_data->cntl_bits,
+			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+		break;
+
+	case MV64XXX_I2C_ACTION_SEND_ADDR_2:
+		writel(drv_data->addr2,
+			drv_data->reg_base + MV64XXX_I2C_REG_DATA);
+		writel(drv_data->cntl_bits,
+			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+		break;
+
+	case MV64XXX_I2C_ACTION_SEND_DATA:
+		writel(drv_data->msg->buf[drv_data->byte_posn++],
+			drv_data->reg_base + MV64XXX_I2C_REG_DATA);
+		writel(drv_data->cntl_bits,
+			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+		break;
+
+	case MV64XXX_I2C_ACTION_RCV_DATA:
+		drv_data->msg->buf[drv_data->byte_posn++] =
+			readl(drv_data->reg_base + MV64XXX_I2C_REG_DATA);
+		writel(drv_data->cntl_bits,
+			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+		break;
+
+	case MV64XXX_I2C_ACTION_RCV_DATA_STOP:
+		drv_data->msg->buf[drv_data->byte_posn++] =
+			readl(drv_data->reg_base + MV64XXX_I2C_REG_DATA);
+		drv_data->cntl_bits &= ~MV64XXX_I2C_REG_CONTROL_INTEN;
+		writel(drv_data->cntl_bits | MV64XXX_I2C_REG_CONTROL_STOP,
+			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+		drv_data->block = 0;
+		wake_up_interruptible(&drv_data->waitq);
+		break;
+
+	case MV64XXX_I2C_ACTION_INVALID:
+	default:
+		printk(KERN_ERR "mv64xxx_i2c_do_action: Invalid action: %d\n",
+			drv_data->action);
+		drv_data->rc = -EIO;
+		/* FALLTHRU */
+	case MV64XXX_I2C_ACTION_SEND_STOP:
+		drv_data->cntl_bits &= ~MV64XXX_I2C_REG_CONTROL_INTEN;
+		writel(drv_data->cntl_bits | MV64XXX_I2C_REG_CONTROL_STOP,
+			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+		drv_data->block = 0;
+		wake_up_interruptible(&drv_data->waitq);
+		break;
+	}
+
+	pr_debug("mv64xxx_i2c_do_action: EXIT\n");
+	return;
+}
+
+static int
+mv64xxx_i2c_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct mv64xxx_i2c_data	*drv_data = dev_id;
+	u32	status;
+	long	flags;
+	int	rc = IRQ_NONE;
+
+	spin_lock_irqsave(&drv_data->lock, flags);
+	while (readl(drv_data->reg_base + MV64XXX_I2C_REG_CONTROL) &
+						MV64XXX_I2C_REG_CONTROL_IFLG) {
+		status = readl(drv_data->reg_base + MV64XXX_I2C_REG_STATUS);
+		mv64xxx_i2c_fsm(drv_data, status);
+		mv64xxx_i2c_do_action(drv_data);
+		rc = IRQ_HANDLED;
+	}
+	spin_unlock_irqrestore(&drv_data->lock, flags);
+
+	return rc;
+}
+
+/*
+ *****************************************************************************
+ *
+ *	I2C Msg Execution Routines
+ *
+ *****************************************************************************
+ */
+static inline void
+mv64xxx_i2c_prepare_for_io(struct mv64xxx_i2c_data *drv_data,
+	struct i2c_msg *msg)
+{
+	u32	dir = 0;
+
+	drv_data->msg = msg;
+	drv_data->byte_posn = 0;
+	drv_data->bytes_left = msg->len;
+	drv_data->rc = 0;
+	drv_data->cntl_bits = MV64XXX_I2C_REG_CONTROL_ACK |
+		MV64XXX_I2C_REG_CONTROL_INTEN | MV64XXX_I2C_REG_CONTROL_TWSIEN;
+
+	if (msg->flags & I2C_M_RD)
+		dir = 1;
+
+	if (msg->flags & I2C_M_REV_DIR_ADDR)
+		dir ^= 1;
+
+	if (msg->flags & I2C_M_TEN) {
+		drv_data->addr1 = 0xf0 | (((u32)msg->addr & 0x300) >> 7) | dir;
+		drv_data->addr2 = (u32)msg->addr & 0xff;
+	}
+	else {
+		drv_data->addr1 = ((u32)msg->addr & 0x7f) << 1 | dir;
+		drv_data->addr2 = 0;
+	}
+
+	return;
+}
+
+static inline void
+mv64xxx_i2c_wait_for_completion(struct mv64xxx_i2c_data *drv_data)
+{
+	long	flags, time_left;
+	char	abort = 0;
+
+	time_left = wait_event_interruptible_timeout(drv_data->waitq,
+		!drv_data->block, msecs_to_jiffies(drv_data->adapter.timeout));
+
+	spin_lock_irqsave(&drv_data->lock, flags);
+	if (!time_left) { /* Timed out */
+		drv_data->rc = -ETIMEDOUT;
+		abort = 1;
+	}
+	else if (time_left < 0) { /* Interrupted/Error */
+		drv_data->rc = time_left; /* errno value */
+		abort = 1;
+	}
+
+	if (abort && drv_data->block) {
+		drv_data->state = MV64XXX_I2C_STATE_ABORTING;
+		spin_unlock_irqrestore(&drv_data->lock, flags);
+
+		time_left = wait_event_timeout(drv_data->waitq,
+			!drv_data->block,
+			msecs_to_jiffies(drv_data->adapter.timeout));
+
+		if (!time_left <= 0) {
+			drv_data->state = MV64XXX_I2C_STATE_IDLE;
+			printk(KERN_WARNING "mv64xxx: I2C bus locked\n");
+		}
+	}
+	else
+		spin_unlock_irqrestore(&drv_data->lock, flags);
+
+	return;
+}
+
+static inline int
+mv64xxx_i2c_execute_msg(struct mv64xxx_i2c_data *drv_data, struct i2c_msg *msg)
+{
+	long	flags;
+
+	spin_lock_irqsave(&drv_data->lock, flags);
+	mv64xxx_i2c_prepare_for_io(drv_data, msg);
+
+	if (unlikely(msg->flags & I2C_M_NOSTART)) { /* Skip start/addr phases */
+		if (drv_data->msg->flags & I2C_M_RD) {
+			/* No action to do, wait for slave to send a byte */
+			drv_data->action = MV64XXX_I2C_ACTION_CONTINUE;
+			drv_data->state =
+				MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_DATA;
+		}
+		else {
+			drv_data->action = MV64XXX_I2C_ACTION_SEND_DATA;
+			drv_data->state =
+				MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_ACK;
+			drv_data->bytes_left--;
+		}
+	}
+	else {
+		drv_data->action = MV64XXX_I2C_ACTION_SEND_START;
+		drv_data->state = MV64XXX_I2C_STATE_WAITING_FOR_START_COND;
+	}
+
+	drv_data->block = 1;
+	mv64xxx_i2c_do_action(drv_data);
+	spin_unlock_irqrestore(&drv_data->lock, flags);
+
+	mv64xxx_i2c_wait_for_completion(drv_data);
+	return drv_data->rc;
+}
+
+/*
+ *****************************************************************************
+ *
+ *	I2C Core Support Routines (Interface to higher level I2C code)
+ *
+ *****************************************************************************
+ */
+static u32
+mv64xxx_i2c_functionality(struct i2c_adapter *adap)
+{
+	return I2C_FUNC_I2C |I2C_FUNC_10BIT_ADDR | I2C_FUNC_SMBUS_EMUL;
+}
+
+static int
+mv64xxx_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
+{
+	struct mv64xxx_i2c_data *drv_data = i2c_get_adapdata(adap);
+	int	i, rc = 0;
+
+	for (i=0; i<num; i++)
+		if ((rc = mv64xxx_i2c_execute_msg(drv_data, &msgs[i])) != 0)
+			break;
+
+	return rc;
+}
+
+static struct i2c_algorithm mv64xxx_i2c_algo = {
+	.name = MV64XXX_I2C_CTLR_NAME "algorithm",
+	.id = I2C_ALGO_MV64XXX,
+	.master_xfer = mv64xxx_i2c_xfer,
+	.functionality = mv64xxx_i2c_functionality,
+};
+
+/*
+ *****************************************************************************
+ *
+ *	Driver Interface & Early Init Routines
+ *
+ *****************************************************************************
+ */
+static void __devinit
+mv64xxx_i2c_hw_init(struct mv64xxx_i2c_data *drv_data)
+{
+	writel(0, drv_data->reg_base + MV64XXX_I2C_REG_SOFT_RESET);
+	writel((((drv_data->freq_m & 0xf) << 3) | (drv_data->freq_n & 0x7)),
+		drv_data->reg_base + MV64XXX_I2C_REG_BAUD);
+	writel(0, drv_data->reg_base + MV64XXX_I2C_REG_SLAVE_ADDR);
+	writel(0, drv_data->reg_base + MV64XXX_I2C_REG_EXT_SLAVE_ADDR);
+	writel(MV64XXX_I2C_REG_CONTROL_TWSIEN | MV64XXX_I2C_REG_CONTROL_STOP,
+		drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+	drv_data->state = MV64XXX_I2C_STATE_IDLE;
+	return;
+}
+
+static int __devinit
+mv64xxx_i2c_map_regs(struct platform_device *pd,
+	struct mv64xxx_i2c_data *drv_data)
+{
+	struct resource	*r;
+
+	if ((r = platform_get_resource(pd, IORESOURCE_MEM, 0)) &&
+		request_mem_region(r->start, MV64XXX_I2C_REG_BLOCK_SIZE,
+			drv_data->adapter.name)) {
+
+		drv_data->reg_base = ioremap(r->start,
+			MV64XXX_I2C_REG_BLOCK_SIZE);
+		drv_data->reg_base_p = r->start;
+	}
+	else
+		return -ENOMEM;
+
+	return 0;
+}
+
+static void __devexit
+mv64xxx_i2c_unmap_regs(struct mv64xxx_i2c_data *drv_data)
+{
+	if (!drv_data->reg_base) {
+		iounmap(drv_data->reg_base);
+		release_mem_region(drv_data->reg_base_p,
+			MV64XXX_I2C_REG_BLOCK_SIZE);
+	}
+
+	drv_data->reg_base = 0;
+	drv_data->reg_base_p = 0;
+	return;
+}
+
+static int __devinit
+mv64xxx_i2c_probe(struct device *dev)
+{
+	struct platform_device		*pd = to_platform_device(dev);
+	struct mv64xxx_i2c_data		*drv_data;
+	struct mv64xxx_i2c_pdata	*pdata = dev->platform_data;
+	int	rc;
+
+	if ((pd->id == 0) && pdata) {
+		drv_data = kmalloc(sizeof(struct mv64xxx_i2c_data), GFP_KERNEL);
+
+		if (!drv_data)
+			return -ENOMEM;
+
+		memset(drv_data, 0, sizeof(struct mv64xxx_i2c_data));
+
+		if (mv64xxx_i2c_map_regs(pd, drv_data)) {
+			kfree(drv_data);
+			return -ENODEV;
+		}
+
+		strncpy(drv_data->adapter.name, MV64XXX_I2C_CTLR_NAME "adapter",
+			I2C_NAME_SIZE);
+
+		init_waitqueue_head(&drv_data->waitq);
+		spin_lock_init(&drv_data->lock);
+
+		drv_data->freq_m = pdata->freq_m;
+		drv_data->freq_n = pdata->freq_n;
+		drv_data->irq = platform_get_irq(pd, 0);
+		drv_data->adapter.id = I2C_ALGO_MV64XXX | I2C_HW_MV64XXX;
+		drv_data->adapter.algo = &mv64xxx_i2c_algo;
+		drv_data->adapter.timeout = pdata->timeout;
+		drv_data->adapter.retries = pdata->retries;
+		dev_set_drvdata(dev, drv_data);
+		i2c_set_adapdata(&drv_data->adapter, drv_data);
+
+		if (request_irq(drv_data->irq, mv64xxx_i2c_intr, 0,
+			MV64XXX_I2C_CTLR_NAME, drv_data)) {
+
+			printk(KERN_ERR "mv64xxx: Can't register intr handler "
+				"irq: %d\\n", drv_data->irq);
+
+			mv64xxx_i2c_unmap_regs(drv_data);
+			kfree(drv_data);
+			return -EINVAL;
+		}
+		else if ((rc = i2c_add_adapter(&drv_data->adapter)) != 0) {
+			printk(KERN_WARNING "mv64xxx: Can't add i2c adapter "
+				"rc: %d\n", -rc);
+			free_irq(drv_data->irq, drv_data);
+			mv64xxx_i2c_unmap_regs(drv_data);
+			kfree(drv_data);
+			return rc;
+		}
+
+		mv64xxx_i2c_hw_init(drv_data);
+	}
+	else
+		return -ENODEV;
+
+	return 0;
+}
+
+static int __devexit
+mv64xxx_i2c_remove(struct device *dev)
+{
+	struct mv64xxx_i2c_data		*drv_data = dev_get_drvdata(dev);
+	int	rc;
+
+	rc = i2c_del_adapter(&drv_data->adapter);
+	free_irq(drv_data->irq, drv_data);
+	mv64xxx_i2c_unmap_regs(drv_data);
+	kfree(drv_data);
+
+	return rc;
+}
+
+static struct device_driver mv64xxx_i2c_driver = {
+	.name	= MV64XXX_I2C_CTLR_NAME,
+	.bus	= &platform_bus_type,
+	.probe	= mv64xxx_i2c_probe,
+	.remove	= mv64xxx_i2c_remove,
+};
+
+static int __devinit
+mv64xxx_i2c_init(void)
+{
+	return driver_register(&mv64xxx_i2c_driver);
+}
+
+static void __devexit
+mv64xxx_i2c_exit(void)
+{
+	driver_unregister(&mv64xxx_i2c_driver);
+	return;
+}
+
+module_init(mv64xxx_i2c_init);
+module_exit(mv64xxx_i2c_exit);
+
+MODULE_AUTHOR("Mark A. Greer <mgreer@mvista.com>");
+MODULE_DESCRIPTION("Marvell mv64xxx host bridge i2c ctlr driver");
+MODULE_LICENSE("GPL");
diff -Nru a/drivers/i2c/busses/i2c-mv64xxx.h b/drivers/i2c/busses/i2c-mv64xxx.h
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/drivers/i2c/busses/i2c-mv64xxx.h	2005-01-26 14:49:22 -07:00
@@ -0,0 +1,99 @@
+/*
+ * drivers/i2c/busses/i2c-mv64xxx.h
+ * 
+ * Driver for the i2c controller on the Marvell line of host bridges for MIPS
+ * and PPC (e.g, gt642[46]0, mv643[46]0, mv644[46]0).
+ *
+ * Author: Mark A. Greer <mgreer@mvista.com>
+ *
+ * 2005 (c) MontaVista, Software, Inc.  This file is licensed under
+ * the terms of the GNU General Public License version 2.  This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+
+#ifndef I2C_MV64XXX_H
+#define I2C_MV64XXX_H
+
+/* Register defines */
+#define	MV64XXX_I2C_REG_SLAVE_ADDR			0x00
+#define	MV64XXX_I2C_REG_DATA				0x04
+#define	MV64XXX_I2C_REG_CONTROL				0x08
+#define	MV64XXX_I2C_REG_STATUS				0x0c
+#define	MV64XXX_I2C_REG_BAUD				0x0c
+#define	MV64XXX_I2C_REG_EXT_SLAVE_ADDR			0x10
+#define	MV64XXX_I2C_REG_SOFT_RESET			0x1c
+
+#define	MV64XXX_I2C_REG_CONTROL_ACK			0x00000004
+#define	MV64XXX_I2C_REG_CONTROL_IFLG			0x00000008
+#define	MV64XXX_I2C_REG_CONTROL_STOP			0x00000010
+#define	MV64XXX_I2C_REG_CONTROL_START			0x00000020
+#define	MV64XXX_I2C_REG_CONTROL_TWSIEN			0x00000040
+#define	MV64XXX_I2C_REG_CONTROL_INTEN			0x00000080
+
+/* Ctlr status values */
+#define	MV64XXX_I2C_STATUS_BUS_ERR			0x00
+#define	MV64XXX_I2C_STATUS_MAST_START			0x08
+#define	MV64XXX_I2C_STATUS_MAST_REPEAT_START		0x10
+#define	MV64XXX_I2C_STATUS_MAST_WR_ADDR_ACK		0x18
+#define	MV64XXX_I2C_STATUS_MAST_WR_ADDR_NO_ACK		0x20
+#define	MV64XXX_I2C_STATUS_MAST_WR_ACK			0x28
+#define	MV64XXX_I2C_STATUS_MAST_WR_NO_ACK		0x30
+#define	MV64XXX_I2C_STATUS_MAST_LOST_ARB		0x38
+#define	MV64XXX_I2C_STATUS_MAST_RD_ADDR_ACK		0x40
+#define	MV64XXX_I2C_STATUS_MAST_RD_ADDR_NO_ACK		0x48
+#define	MV64XXX_I2C_STATUS_MAST_RD_DATA_ACK		0x50
+#define	MV64XXX_I2C_STATUS_MAST_RD_DATA_NO_ACK		0x58
+#define	MV64XXX_I2C_STATUS_MAST_WR_ADDR_2_ACK		0xd0
+#define	MV64XXX_I2C_STATUS_MAST_WR_ADDR_2_NO_ACK	0xd8
+#define	MV64XXX_I2C_STATUS_MAST_RD_ADDR_2_ACK		0xe0
+#define	MV64XXX_I2C_STATUS_MAST_RD_ADDR_2_NO_ACK	0xe8
+#define	MV64XXX_I2C_STATUS_NO_STATUS			0xf8
+
+/* Driver states */
+enum {
+	MV64XXX_I2C_STATE_INVALID,
+	MV64XXX_I2C_STATE_IDLE,
+	MV64XXX_I2C_STATE_WAITING_FOR_START_COND,
+	MV64XXX_I2C_STATE_WAITING_FOR_ADDR_1_ACK,
+	MV64XXX_I2C_STATE_WAITING_FOR_ADDR_2_ACK,
+	MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_ACK,
+	MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_DATA,
+	MV64XXX_I2C_STATE_ABORTING,
+};
+
+/* Driver actions */
+enum {
+	MV64XXX_I2C_ACTION_INVALID,
+	MV64XXX_I2C_ACTION_CONTINUE,
+	MV64XXX_I2C_ACTION_SEND_START,
+	MV64XXX_I2C_ACTION_SEND_ADDR_1,
+	MV64XXX_I2C_ACTION_SEND_ADDR_2,
+	MV64XXX_I2C_ACTION_SEND_DATA,
+	MV64XXX_I2C_ACTION_RCV_DATA,
+	MV64XXX_I2C_ACTION_RCV_DATA_STOP,
+	MV64XXX_I2C_ACTION_SEND_STOP,
+};
+
+struct mv64xxx_i2c_data {
+	int			irq;
+	uint			state;
+	uint			action;
+	u32			cntl_bits;
+	void			*reg_base;
+	ulong			reg_base_p;
+	u32			addr1;
+	u32			addr2;
+	uint			bytes_left;
+	uint			byte_posn;
+	uint			block;
+	int			rc;
+	u32			freq_m;
+	u32			freq_n;
+	wait_queue_head_t	waitq;
+	spinlock_t		lock;
+	struct i2c_msg		*msg;
+	struct i2c_adapter	adapter;
+};
+
+#endif /* I2C_MV64XXX_H */
diff -Nru a/include/linux/i2c-id.h b/include/linux/i2c-id.h
--- a/include/linux/i2c-id.h	2005-01-26 14:49:22 -07:00
+++ b/include/linux/i2c-id.h	2005-01-26 14:49:22 -07:00
@@ -200,6 +200,9 @@
 
 #define I2C_ALGO_SIBYTE 0x150000	/* Broadcom SiByte SOCs		*/
 #define I2C_ALGO_SGI	0x160000        /* SGI algorithm                */
+					/* 0x170000 - USB		*/
+					/* 0x180000 - Virtual buses	*/
+#define I2C_ALGO_MV64XXX 0x190000       /* Marvell mv64xxx i2c ctlr	*/
 
 #define I2C_ALGO_EXP	0x800000	/* experimental			*/
 
@@ -304,5 +307,8 @@
 
 /* --- MCP107 adapter */
 #define I2C_HW_MPC107 0x00
+
+/* --- Marvell mv64xxx i2c adapter */
+#define I2C_HW_MV64XXX 0x00
 
 #endif /* LINUX_I2C_ID_H */
diff -Nru a/include/linux/mv643xx.h b/include/linux/mv643xx.h
--- a/include/linux/mv643xx.h	2005-01-26 14:49:22 -07:00
+++ b/include/linux/mv643xx.h	2005-01-26 14:49:22 -07:00
@@ -977,12 +977,9 @@
 /* I2C Registers                        */
 /****************************************/
 
-#define MV64340_I2C_SLAVE_ADDR                                      0xc000
-#define MV64340_I2C_EXTENDED_SLAVE_ADDR                             0xc010
-#define MV64340_I2C_DATA                                            0xc004
-#define MV64340_I2C_CONTROL                                         0xc008
-#define MV64340_I2C_STATUS_BAUDE_RATE                               0xc00C
-#define MV64340_I2C_SOFT_RESET                                      0xc01c
+#define MV64XXX_I2C_CTLR_NAME					"mv64xxx i2c"
+#define MV64XXX_I2C_OFFSET                                          0xc000
+#define MV64XXX_I2C_REG_BLOCK_SIZE                                  0x0020
 
 /****************************************/
 /* GPP Interface Registers              */
@@ -1083,6 +1080,14 @@
 	u8	brg_can_tune;
 	u8	brg_clk_src;
 	u32	brg_clk_freq;
+};
+
+/* i2c Platform Device, Driver Data */
+struct mv64xxx_i2c_pdata {
+	u32	freq_m;
+	u32	freq_n;
+	u32	timeout;	/* In milliseconds */
+	u32	retries;
 };
 
 #endif /* __ASM_MV64340_H */

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

* Re: [PATCH][I2C] Marvell mv64xxx i2c driver
  2005-01-26 19:56 ` Jean Delvare
@ 2005-01-26 20:33   ` Mark A. Greer
  2005-01-26 21:56   ` Mark A. Greer
  1 sibling, 0 replies; 23+ messages in thread
From: Mark A. Greer @ 2005-01-26 20:33 UTC (permalink / raw)
  To: LM Sensors, LKML; +Cc: Greg KH

Jean Delvare wrote:

>Hi Mark,
>

Thanks for the commenting, Jean.

><snip>
>
>  
>
>>+config I2C_MV64XXX
>>+	tristate "Marvell mv64xxx I2C Controller"
>>+	depends on I2C && MV64X60
>>    
>>
>
>&& EXPERIMENTAL?
>

Yes, I guess that's the correct thing to do.  I'll add that.

>>diff -Nru a/include/linux/i2c-id.h b/include/linux/i2c-id.h
>>--- a/include/linux/i2c-id.h	2005-01-25 18:15:24 -07:00
>>+++ b/include/linux/i2c-id.h	2005-01-25 18:15:24 -07:00
>>@@ -200,6 +200,7 @@
>> 
>> #define I2C_ALGO_SIBYTE 0x150000	/* Broadcom SiByte SOCs		*/
>> #define I2C_ALGO_SGI	0x160000        /* SGI algorithm                */
>>+#define I2C_ALGO_MV64XXX 0x170000       /* Marvell mv64xxx i2c ctlr     */
>>    
>>
>
>0x170000 is reserved within the legacy i2c project for an USB algorithm,
>and 0x180000 for virtual busses. Could you please use 0x190000 instead,
>so as to avoid future collisions?
>

Absolutely.  I was unaware of the other uses.

>  
>
>>-#define MV64340_I2C_SOFT_RESET                                      0xc01c
>>+#define	MV64XXX_I2C_CTLR_NAME					"mv64xxx i2c"
>>+#define MV64XXX_I2C_OFFSET					    0xc000
>>+#define MV64XXX_I2C_REG_BLOCK_SIZE				    0x0020
>>    
>>
>
>You have a tab instead of space before MV64XXX_I2C_CTLR_NAME, it seems.
>Also, you want to align the numerical values using only tabs, no space.
>  
>

Oops.  I'll fix that too and repost later today.

Mark


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

* Re: [PATCH][I2C] Marvell mv64xxx i2c driver
  2005-01-26  1:26 Mark A. Greer
@ 2005-01-26 19:56 ` Jean Delvare
  2005-01-26 20:33   ` Mark A. Greer
  2005-01-26 21:56   ` Mark A. Greer
  2005-01-31 18:25 ` Greg KH
  1 sibling, 2 replies; 23+ messages in thread
From: Jean Delvare @ 2005-01-26 19:56 UTC (permalink / raw)
  To: Mark A. Greer; +Cc: Greg KH, LM Sensors, LKML

Hi Mark,

> Marvell makes a line of host bridge for PPC and MIPS systems.  On
> those  bridges is an i2c controller.  This patch adds the driver for
> that i2c  controller.
> 
> Please let me know if you see any problems with this patch.

I do not feel qualified for a full review of this code. However, I
noticed the following minor issues:

> +config I2C_MV64XXX
> +	tristate "Marvell mv64xxx I2C Controller"
> +	depends on I2C && MV64X60

&& EXPERIMENTAL?

> diff -Nru a/include/linux/i2c-id.h b/include/linux/i2c-id.h
> --- a/include/linux/i2c-id.h	2005-01-25 18:15:24 -07:00
> +++ b/include/linux/i2c-id.h	2005-01-25 18:15:24 -07:00
> @@ -200,6 +200,7 @@
>  
>  #define I2C_ALGO_SIBYTE 0x150000	/* Broadcom SiByte SOCs		*/
>  #define I2C_ALGO_SGI	0x160000        /* SGI algorithm                */
> +#define I2C_ALGO_MV64XXX 0x170000       /* Marvell mv64xxx i2c ctlr     */

0x170000 is reserved within the legacy i2c project for an USB algorithm,
and 0x180000 for virtual busses. Could you please use 0x190000 instead,
so as to avoid future collisions?

> -#define MV64340_I2C_SLAVE_ADDR                                      0xc000
> -#define MV64340_I2C_EXTENDED_SLAVE_ADDR                             0xc010
> -#define MV64340_I2C_DATA                                            0xc004
> -#define MV64340_I2C_CONTROL                                         0xc008
> -#define MV64340_I2C_STATUS_BAUDE_RATE                               0xc00C
> -#define MV64340_I2C_SOFT_RESET                                      0xc01c
> +#define	MV64XXX_I2C_CTLR_NAME					"mv64xxx i2c"
> +#define MV64XXX_I2C_OFFSET					    0xc000
> +#define MV64XXX_I2C_REG_BLOCK_SIZE				    0x0020

You have a tab instead of space before MV64XXX_I2C_CTLR_NAME, it seems.
Also, you want to align the numerical values using only tabs, no space.

Thanks,
-- 
Jean Delvare
http://khali.linux-fr.org/

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

* [PATCH][I2C] Marvell mv64xxx i2c driver
@ 2005-01-26  1:26 Mark A. Greer
  2005-01-26 19:56 ` Jean Delvare
  2005-01-31 18:25 ` Greg KH
  0 siblings, 2 replies; 23+ messages in thread
From: Mark A. Greer @ 2005-01-26  1:26 UTC (permalink / raw)
  To: Greg KH, phil; +Cc: sensors, lkml

[-- Attachment #1: Type: text/plain, Size: 372 bytes --]

Greg, Philip,

Marvell makes a line of host bridge for PPC and MIPS systems.  On those 
bridges is an i2c controller.  This patch adds the driver for that i2c 
controller.

Please let me know if you see any problems with this patch.

Also, if you're not the correct person(s), please point me to who is.

Thanks,

Mark

Signed-off-by: Mark A. Greer <mgreer@mvista.com>
--

[-- Attachment #2: i2c.patch --]
[-- Type: text/plain, Size: 22326 bytes --]

diff -Nru a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
--- a/drivers/i2c/busses/Kconfig	2005-01-25 18:15:24 -07:00
+++ b/drivers/i2c/busses/Kconfig	2005-01-25 18:15:24 -07:00
@@ -476,4 +476,14 @@
 	  This driver can also be built as a module.  If so, the module
 	  will be called i2c-pca-isa.
 
+config I2C_MV64XXX
+	tristate "Marvell mv64xxx I2C Controller"
+	depends on I2C && MV64X60
+	help
+	  If you say yes to this option, support will be included for the
+	  built-in I2C interface on the Marvell 64xxx line of host bridges
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called i2c-mv64xxx.
+
 endmenu
diff -Nru a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
--- a/drivers/i2c/busses/Makefile	2005-01-25 18:15:24 -07:00
+++ b/drivers/i2c/busses/Makefile	2005-01-25 18:15:24 -07:00
@@ -20,6 +20,7 @@
 obj-$(CONFIG_I2C_IXP4XX)	+= i2c-ixp4xx.o
 obj-$(CONFIG_I2C_KEYWEST)	+= i2c-keywest.o
 obj-$(CONFIG_I2C_MPC)		+= i2c-mpc.o
+obj-$(CONFIG_I2C_MV64XXX)	+= i2c-mv64xxx.o
 obj-$(CONFIG_I2C_NFORCE2)	+= i2c-nforce2.o
 obj-$(CONFIG_I2C_PARPORT)	+= i2c-parport.o
 obj-$(CONFIG_I2C_PARPORT_LIGHT)	+= i2c-parport-light.o
diff -Nru a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/drivers/i2c/busses/i2c-mv64xxx.c	2005-01-25 18:15:24 -07:00
@@ -0,0 +1,550 @@
+/*
+ * drivers/i2c/busses/i2c-mv64xxx.c
+ * 
+ * Driver for the i2c controller on the Marvell line of host bridges for MIPS
+ * and PPC (e.g, gt642[46]0, mv643[46]0, mv644[46]0).
+ *
+ * Author: Mark A. Greer <mgreer@mvista.com>
+ *
+ * 2005 (c) MontaVista, Software, Inc.  This file is licensed under
+ * the terms of the GNU General Public License version 2.  This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/wait.h>
+#include <linux/spinlock.h>
+#include <asm/io.h>
+#include <asm/ocp.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/mv643xx.h>
+#include "i2c-mv64xxx.h"
+
+/*
+ *****************************************************************************
+ *
+ *	Finite State Machine & Interrupt Routines
+ *
+ *****************************************************************************
+ */
+static inline void
+mv64xxx_i2c_fsm(struct mv64xxx_i2c_data *drv_data, u32 status)
+{
+	pr_debug("mv64xxx_i2c_fsm: ENTER--state: %d, status: 0x%x\n",
+		drv_data->state, status);
+
+	/*
+	 * If state is idle, then this is likely the remnants of an old
+	 * operation that driver has given up on or the user has killed.
+	 * If so, issue the stop condition and go to idle.
+	 */
+	if (drv_data->state == MV64XXX_I2C_STATE_IDLE) {
+		drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
+		drv_data->state = MV64XXX_I2C_STATE_IDLE;
+		pr_debug("mv64xxx_i2c_fsm: EXIT--Entered when in IDLE state\n");
+		return;
+	}
+
+	if (drv_data->state == MV64XXX_I2C_STATE_ABORTING) {
+		drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
+		drv_data->state = MV64XXX_I2C_STATE_IDLE;
+		pr_debug("mv64xxx_i2c_fsm: EXIT--Aborting\n");
+		return;
+	}
+
+	/* The status from the ctlr [mostly] tells us what to do next */
+	switch (status) {
+	/* Start condition interrupt */
+	case MV64XXX_I2C_STATUS_MAST_START: /* 0x08 */
+	case MV64XXX_I2C_STATUS_MAST_REPEAT_START: /* 0x10 */
+		drv_data->action = MV64XXX_I2C_ACTION_SEND_ADDR_1;
+		drv_data->state = MV64XXX_I2C_STATE_WAITING_FOR_ADDR_1_ACK;
+		break;
+
+	/* Performing a write */
+	case MV64XXX_I2C_STATUS_MAST_WR_ADDR_ACK: /* 0x18 */
+		if (drv_data->msg->flags & I2C_M_TEN) {
+			drv_data->action = MV64XXX_I2C_ACTION_SEND_ADDR_2;
+			drv_data->state =
+				MV64XXX_I2C_STATE_WAITING_FOR_ADDR_2_ACK;
+			break;
+		}
+		/* FALLTHRU */
+	case MV64XXX_I2C_STATUS_MAST_WR_ADDR_2_ACK: /* 0xd0 */
+	case MV64XXX_I2C_STATUS_MAST_WR_ACK: /* 0x28 */
+		if (drv_data->bytes_left > 0) {
+			drv_data->action = MV64XXX_I2C_ACTION_SEND_DATA;
+			drv_data->state =
+				MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_ACK;
+			drv_data->bytes_left--;
+		}
+		else {
+			drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
+			drv_data->state = MV64XXX_I2C_STATE_IDLE;
+		}
+		break;
+
+	/* Performing a read */
+	case MV64XXX_I2C_STATUS_MAST_RD_ADDR_ACK: /* 40 */
+		if (drv_data->msg->flags & I2C_M_TEN) {
+			drv_data->action = MV64XXX_I2C_ACTION_SEND_ADDR_2;
+			drv_data->state =
+				MV64XXX_I2C_STATE_WAITING_FOR_ADDR_2_ACK;
+			break;
+		}
+		/* FALLTHRU */
+	case MV64XXX_I2C_STATUS_MAST_RD_ADDR_2_ACK: /* 0xe0 */
+		if (drv_data->bytes_left == 0) {
+			drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
+			drv_data->state = MV64XXX_I2C_STATE_IDLE;
+			break;
+		}
+		/* FALLTHRU */
+	case MV64XXX_I2C_STATUS_MAST_RD_DATA_ACK: /* 0x50 */
+		if (status != MV64XXX_I2C_STATUS_MAST_RD_DATA_ACK)
+			drv_data->action = MV64XXX_I2C_ACTION_CONTINUE;
+		else {
+			drv_data->action = MV64XXX_I2C_ACTION_RCV_DATA;
+			drv_data->bytes_left--;
+		}
+		drv_data->state = MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_DATA;
+
+		if (drv_data->bytes_left == 1)
+			drv_data->cntl_bits &= ~MV64XXX_I2C_REG_CONTROL_ACK;
+		break;
+
+	case MV64XXX_I2C_STATUS_MAST_RD_DATA_NO_ACK: /* 0x58 */
+		drv_data->action = MV64XXX_I2C_ACTION_RCV_DATA_STOP;
+		drv_data->state = MV64XXX_I2C_STATE_IDLE;
+		break;
+
+	case MV64XXX_I2C_STATUS_MAST_WR_ADDR_NO_ACK: /* 0x20 */
+	case MV64XXX_I2C_STATUS_MAST_WR_NO_ACK: /* 30 */
+	case MV64XXX_I2C_STATUS_MAST_RD_ADDR_NO_ACK: /* 48 */
+		/* Doesn't seem to be a device at other end */
+		drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
+		drv_data->state = MV64XXX_I2C_STATE_IDLE;
+		drv_data->rc = -ENODEV;
+		break;
+
+	default:
+		printk(KERN_ERR "mv64xxx_i2c_fsm: Ctlr Error -- "
+			"state: 0x%x, status: 0x%x\n", drv_data->state, status);
+		printk(KERN_INFO "addr: 0x%x, flags: 0x%x\n",
+			drv_data->msg->addr, drv_data->msg->flags);
+		drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
+		drv_data->state = MV64XXX_I2C_STATE_IDLE;
+		drv_data->rc = -EIO;
+	}
+
+	pr_debug("mv64xxx_i2c_fsm: EXIT--action: %d, state: %d, rc: 0x%x\n",
+		drv_data->action, drv_data->state, drv_data->rc);
+	return;
+}
+
+static void
+mv64xxx_i2c_do_action(struct mv64xxx_i2c_data *drv_data)
+{
+	pr_debug("mv64xxx_i2c_do_action: ENTER--action: %d, state: %d, "
+		"cntl: 0x%x\n", drv_data->action, drv_data->state,
+		drv_data->cntl_bits);
+
+	switch(drv_data->action) {
+	case MV64XXX_I2C_ACTION_CONTINUE:
+		writel(drv_data->cntl_bits,
+			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+		break;
+
+	case MV64XXX_I2C_ACTION_SEND_START:
+		writel(drv_data->cntl_bits | MV64XXX_I2C_REG_CONTROL_START,
+			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+		break;
+
+	case MV64XXX_I2C_ACTION_SEND_ADDR_1:
+		writel(drv_data->addr1,
+			drv_data->reg_base + MV64XXX_I2C_REG_DATA);
+		writel(drv_data->cntl_bits,
+			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+		break;
+
+	case MV64XXX_I2C_ACTION_SEND_ADDR_2:
+		writel(drv_data->addr2,
+			drv_data->reg_base + MV64XXX_I2C_REG_DATA);
+		writel(drv_data->cntl_bits,
+			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+		break;
+
+	case MV64XXX_I2C_ACTION_SEND_DATA:
+		writel(drv_data->msg->buf[drv_data->byte_posn++],
+			drv_data->reg_base + MV64XXX_I2C_REG_DATA);
+		writel(drv_data->cntl_bits,
+			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+		break;
+
+	case MV64XXX_I2C_ACTION_RCV_DATA:
+		drv_data->msg->buf[drv_data->byte_posn++] =
+			readl(drv_data->reg_base + MV64XXX_I2C_REG_DATA);
+		writel(drv_data->cntl_bits,
+			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+		break;
+
+	case MV64XXX_I2C_ACTION_RCV_DATA_STOP:
+		drv_data->msg->buf[drv_data->byte_posn++] =
+			readl(drv_data->reg_base + MV64XXX_I2C_REG_DATA);
+		drv_data->cntl_bits &= ~MV64XXX_I2C_REG_CONTROL_INTEN;
+		writel(drv_data->cntl_bits | MV64XXX_I2C_REG_CONTROL_STOP,
+			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+		drv_data->block = 0;
+		wake_up_interruptible(&drv_data->waitq);
+		break;
+
+	case MV64XXX_I2C_ACTION_INVALID:
+	default:
+		printk(KERN_ERR "mv64xxx_i2c_do_action: Invalid action: %d\n",
+			drv_data->action);
+		drv_data->rc = -EIO;
+		/* FALLTHRU */
+	case MV64XXX_I2C_ACTION_SEND_STOP:
+		drv_data->cntl_bits &= ~MV64XXX_I2C_REG_CONTROL_INTEN;
+		writel(drv_data->cntl_bits | MV64XXX_I2C_REG_CONTROL_STOP,
+			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+		drv_data->block = 0;
+		wake_up_interruptible(&drv_data->waitq);
+		break;
+	}
+
+	pr_debug("mv64xxx_i2c_do_action: EXIT\n");
+	return;
+}
+
+static int
+mv64xxx_i2c_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct mv64xxx_i2c_data	*drv_data = dev_id;
+	u32	status;
+	long	flags;
+	int	rc = IRQ_NONE;
+
+	spin_lock_irqsave(&drv_data->lock, flags);
+	while (readl(drv_data->reg_base + MV64XXX_I2C_REG_CONTROL) &
+						MV64XXX_I2C_REG_CONTROL_IFLG) {
+		status = readl(drv_data->reg_base + MV64XXX_I2C_REG_STATUS);
+		mv64xxx_i2c_fsm(drv_data, status);
+		mv64xxx_i2c_do_action(drv_data);
+		rc = IRQ_HANDLED;
+	}
+	spin_unlock_irqrestore(&drv_data->lock, flags);
+
+	return rc;
+}
+
+/*
+ *****************************************************************************
+ *
+ *	I2C Msg Execution Routines
+ *
+ *****************************************************************************
+ */
+static inline void
+mv64xxx_i2c_prepare_for_io(struct mv64xxx_i2c_data *drv_data,
+	struct i2c_msg *msg)
+{
+	u32	dir = 0;
+
+	drv_data->msg = msg;
+	drv_data->byte_posn = 0;
+	drv_data->bytes_left = msg->len;
+	drv_data->rc = 0;
+	drv_data->cntl_bits = MV64XXX_I2C_REG_CONTROL_ACK |
+		MV64XXX_I2C_REG_CONTROL_INTEN | MV64XXX_I2C_REG_CONTROL_TWSIEN;
+
+	if (msg->flags & I2C_M_RD)
+		dir = 1;
+
+	if (msg->flags & I2C_M_REV_DIR_ADDR)
+		dir ^= 1;
+
+	if (msg->flags & I2C_M_TEN) {
+		drv_data->addr1 = 0xf0 | (((u32)msg->addr & 0x300) >> 7) | dir;
+		drv_data->addr2 = (u32)msg->addr & 0xff;
+	}
+	else {
+		drv_data->addr1 = ((u32)msg->addr & 0x7f) << 1 | dir;
+		drv_data->addr2 = 0;
+	}
+
+	return;
+}
+
+static inline void
+mv64xxx_i2c_wait_for_completion(struct mv64xxx_i2c_data *drv_data)
+{
+	long	flags, time_left;
+	char	abort = 0;
+
+	time_left = wait_event_interruptible_timeout(drv_data->waitq,
+		!drv_data->block, msecs_to_jiffies(drv_data->adapter.timeout));
+
+	spin_lock_irqsave(&drv_data->lock, flags);
+	if (!time_left) { /* Timed out */
+		drv_data->rc = -ETIMEDOUT;
+		abort = 1;
+	}
+	else if (time_left < 0) { /* Interrupted/Error */
+		drv_data->rc = time_left; /* errno value */
+		abort = 1;
+	}
+
+	if (abort && drv_data->block) {
+		drv_data->state = MV64XXX_I2C_STATE_ABORTING;
+		spin_unlock_irqrestore(&drv_data->lock, flags);
+
+		time_left = wait_event_timeout(drv_data->waitq,
+			!drv_data->block,
+			msecs_to_jiffies(drv_data->adapter.timeout));
+
+		if (!time_left <= 0) {
+			drv_data->state = MV64XXX_I2C_STATE_IDLE;
+			printk(KERN_WARNING "mv64xxx: I2C bus locked\n");
+		}
+	}
+	else
+		spin_unlock_irqrestore(&drv_data->lock, flags);
+
+	return;
+}
+
+static inline int
+mv64xxx_i2c_execute_msg(struct mv64xxx_i2c_data *drv_data, struct i2c_msg *msg)
+{
+	long	flags;
+
+	spin_lock_irqsave(&drv_data->lock, flags);
+	mv64xxx_i2c_prepare_for_io(drv_data, msg);
+
+	if (unlikely(msg->flags & I2C_M_NOSTART)) { /* Skip start/addr phases */
+		if (drv_data->msg->flags & I2C_M_RD) {
+			/* No action to do, wait for slave to send a byte */
+			drv_data->action = MV64XXX_I2C_ACTION_CONTINUE;
+			drv_data->state =
+				MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_DATA;
+		}
+		else {
+			drv_data->action = MV64XXX_I2C_ACTION_SEND_DATA;
+			drv_data->state =
+				MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_ACK;
+			drv_data->bytes_left--;
+		}
+	}
+	else {
+		drv_data->action = MV64XXX_I2C_ACTION_SEND_START;
+		drv_data->state = MV64XXX_I2C_STATE_WAITING_FOR_START_COND;
+	}
+
+	drv_data->block = 1;
+	mv64xxx_i2c_do_action(drv_data);
+	spin_unlock_irqrestore(&drv_data->lock, flags);
+
+	mv64xxx_i2c_wait_for_completion(drv_data);
+	return drv_data->rc;
+}
+
+/*
+ *****************************************************************************
+ *
+ *	I2C Core Support Routines (Interface to higher level I2C code)
+ *
+ *****************************************************************************
+ */
+static u32
+mv64xxx_i2c_functionality(struct i2c_adapter *adap)
+{
+	return I2C_FUNC_I2C |I2C_FUNC_10BIT_ADDR | I2C_FUNC_SMBUS_EMUL;
+}
+
+static int
+mv64xxx_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
+{
+	struct mv64xxx_i2c_data *drv_data = i2c_get_adapdata(adap);
+	int	i, rc = 0;
+
+	for (i=0; i<num; i++)
+		if ((rc = mv64xxx_i2c_execute_msg(drv_data, &msgs[i])) != 0)
+			break;
+
+	return rc;
+}
+
+static struct i2c_algorithm mv64xxx_i2c_algo = {
+	.name = MV64XXX_I2C_CTLR_NAME "algorithm",
+	.id = I2C_ALGO_MV64XXX,
+	.master_xfer = mv64xxx_i2c_xfer,
+	.functionality = mv64xxx_i2c_functionality,
+};
+
+/*
+ *****************************************************************************
+ *
+ *	Driver Interface & Early Init Routines
+ *
+ *****************************************************************************
+ */
+static void __devinit
+mv64xxx_i2c_hw_init(struct mv64xxx_i2c_data *drv_data)
+{
+	writel(0, drv_data->reg_base + MV64XXX_I2C_REG_SOFT_RESET);
+	writel((((drv_data->freq_m & 0xf) << 3) | (drv_data->freq_n & 0x7)),
+		drv_data->reg_base + MV64XXX_I2C_REG_BAUD);
+	writel(0, drv_data->reg_base + MV64XXX_I2C_REG_SLAVE_ADDR);
+	writel(0, drv_data->reg_base + MV64XXX_I2C_REG_EXT_SLAVE_ADDR);
+	writel(MV64XXX_I2C_REG_CONTROL_TWSIEN | MV64XXX_I2C_REG_CONTROL_STOP,
+		drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+	drv_data->state = MV64XXX_I2C_STATE_IDLE;
+	return;
+}
+
+static int __devinit
+mv64xxx_i2c_map_regs(struct platform_device *pd,
+	struct mv64xxx_i2c_data *drv_data)
+{
+	struct resource	*r;
+
+	if ((r = platform_get_resource(pd, IORESOURCE_MEM, 0)) &&
+		request_mem_region(r->start, MV64XXX_I2C_REG_BLOCK_SIZE,
+			drv_data->adapter.name)) {
+
+		drv_data->reg_base = ioremap(r->start,
+			MV64XXX_I2C_REG_BLOCK_SIZE);
+		drv_data->reg_base_p = r->start;
+	}
+	else
+		return -ENOMEM;
+
+	return 0;
+}
+
+static void __devexit
+mv64xxx_i2c_unmap_regs(struct mv64xxx_i2c_data *drv_data)
+{
+	if (!drv_data->reg_base) {
+		iounmap(drv_data->reg_base);
+		release_mem_region(drv_data->reg_base_p,
+			MV64XXX_I2C_REG_BLOCK_SIZE);
+	}
+
+	drv_data->reg_base = 0;
+	drv_data->reg_base_p = 0;
+	return;
+}
+
+static int __devinit
+mv64xxx_i2c_probe(struct device *dev)
+{
+	struct platform_device		*pd = to_platform_device(dev);
+	struct mv64xxx_i2c_data		*drv_data;
+	struct mv64xxx_i2c_pdata	*pdata = dev->platform_data;
+	int	rc;
+
+	if ((pd->id == 0) && pdata) {
+		drv_data = kmalloc(sizeof(struct mv64xxx_i2c_data), GFP_KERNEL);
+
+		if (!drv_data)
+			return -ENOMEM;
+
+		memset(drv_data, 0, sizeof(struct mv64xxx_i2c_data));
+
+		if (mv64xxx_i2c_map_regs(pd, drv_data)) {
+			kfree(drv_data);
+			return -ENODEV;
+		}
+
+		strncpy(drv_data->adapter.name, MV64XXX_I2C_CTLR_NAME "adapter",
+			I2C_NAME_SIZE);
+
+		init_waitqueue_head(&drv_data->waitq);
+		spin_lock_init(&drv_data->lock);
+
+		drv_data->freq_m = pdata->freq_m;
+		drv_data->freq_n = pdata->freq_n;
+		drv_data->irq = platform_get_irq(pd, 0);
+		drv_data->adapter.id = I2C_ALGO_MV64XXX | I2C_HW_MV64XXX;
+		drv_data->adapter.algo = &mv64xxx_i2c_algo;
+		drv_data->adapter.timeout = pdata->timeout;
+		drv_data->adapter.retries = pdata->retries;
+		dev_set_drvdata(dev, drv_data);
+		i2c_set_adapdata(&drv_data->adapter, drv_data);
+
+		if (request_irq(drv_data->irq, mv64xxx_i2c_intr, 0,
+			MV64XXX_I2C_CTLR_NAME, drv_data)) {
+
+			printk(KERN_ERR "mv64xxx: Can't register intr handler "
+				"irq: %d\\n", drv_data->irq);
+
+			mv64xxx_i2c_unmap_regs(drv_data);
+			kfree(drv_data);
+			return -EINVAL;
+		}
+		else if ((rc = i2c_add_adapter(&drv_data->adapter)) != 0) {
+			printk(KERN_WARNING "mv64xxx: Can't add i2c adapter "
+				"rc: %d\n", -rc);
+			free_irq(drv_data->irq, drv_data);
+			mv64xxx_i2c_unmap_regs(drv_data);
+			kfree(drv_data);
+			return rc;
+		}
+
+		mv64xxx_i2c_hw_init(drv_data);
+	}
+	else
+		return -ENODEV;
+
+	return 0;
+}
+
+static int __devexit
+mv64xxx_i2c_remove(struct device *dev)
+{
+	struct mv64xxx_i2c_data		*drv_data = dev_get_drvdata(dev);
+	int	rc;
+
+	rc = i2c_del_adapter(&drv_data->adapter);
+	free_irq(drv_data->irq, drv_data);
+	mv64xxx_i2c_unmap_regs(drv_data);
+	kfree(drv_data);
+
+	return rc;
+}
+
+static struct device_driver mv64xxx_i2c_driver = {
+	.name	= MV64XXX_I2C_CTLR_NAME,
+	.bus	= &platform_bus_type,
+	.probe	= mv64xxx_i2c_probe,
+	.remove	= mv64xxx_i2c_remove,
+};
+
+static int __devinit
+mv64xxx_i2c_init(void)
+{
+	return driver_register(&mv64xxx_i2c_driver);
+}
+
+static void __devexit
+mv64xxx_i2c_exit(void)
+{
+	driver_unregister(&mv64xxx_i2c_driver);
+	return;
+}
+
+module_init(mv64xxx_i2c_init);
+module_exit(mv64xxx_i2c_exit);
+
+MODULE_AUTHOR("Mark A. Greer <mgreer@mvista.com>");
+MODULE_DESCRIPTION("Marvell mv64xxx host bridge i2c ctlr driver");
+MODULE_LICENSE("GPL");
diff -Nru a/drivers/i2c/busses/i2c-mv64xxx.h b/drivers/i2c/busses/i2c-mv64xxx.h
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/drivers/i2c/busses/i2c-mv64xxx.h	2005-01-25 18:15:24 -07:00
@@ -0,0 +1,99 @@
+/*
+ * drivers/i2c/busses/i2c-mv64xxx.h
+ * 
+ * Driver for the i2c controller on the Marvell line of host bridges for MIPS
+ * and PPC (e.g, gt642[46]0, mv643[46]0, mv644[46]0).
+ *
+ * Author: Mark A. Greer <mgreer@mvista.com>
+ *
+ * 2005 (c) MontaVista, Software, Inc.  This file is licensed under
+ * the terms of the GNU General Public License version 2.  This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+
+#ifndef I2C_MV64XXX_H
+#define I2C_MV64XXX_H
+
+/* Register defines */
+#define	MV64XXX_I2C_REG_SLAVE_ADDR			0x00
+#define	MV64XXX_I2C_REG_DATA				0x04
+#define	MV64XXX_I2C_REG_CONTROL				0x08
+#define	MV64XXX_I2C_REG_STATUS				0x0c
+#define	MV64XXX_I2C_REG_BAUD				0x0c
+#define	MV64XXX_I2C_REG_EXT_SLAVE_ADDR			0x10
+#define	MV64XXX_I2C_REG_SOFT_RESET			0x1c
+
+#define	MV64XXX_I2C_REG_CONTROL_ACK			0x00000004
+#define	MV64XXX_I2C_REG_CONTROL_IFLG			0x00000008
+#define	MV64XXX_I2C_REG_CONTROL_STOP			0x00000010
+#define	MV64XXX_I2C_REG_CONTROL_START			0x00000020
+#define	MV64XXX_I2C_REG_CONTROL_TWSIEN			0x00000040
+#define	MV64XXX_I2C_REG_CONTROL_INTEN			0x00000080
+
+/* Ctlr status values */
+#define	MV64XXX_I2C_STATUS_BUS_ERR			0x00
+#define	MV64XXX_I2C_STATUS_MAST_START			0x08
+#define	MV64XXX_I2C_STATUS_MAST_REPEAT_START		0x10
+#define	MV64XXX_I2C_STATUS_MAST_WR_ADDR_ACK		0x18
+#define	MV64XXX_I2C_STATUS_MAST_WR_ADDR_NO_ACK		0x20
+#define	MV64XXX_I2C_STATUS_MAST_WR_ACK			0x28
+#define	MV64XXX_I2C_STATUS_MAST_WR_NO_ACK		0x30
+#define	MV64XXX_I2C_STATUS_MAST_LOST_ARB		0x38
+#define	MV64XXX_I2C_STATUS_MAST_RD_ADDR_ACK		0x40
+#define	MV64XXX_I2C_STATUS_MAST_RD_ADDR_NO_ACK		0x48
+#define	MV64XXX_I2C_STATUS_MAST_RD_DATA_ACK		0x50
+#define	MV64XXX_I2C_STATUS_MAST_RD_DATA_NO_ACK		0x58
+#define	MV64XXX_I2C_STATUS_MAST_WR_ADDR_2_ACK		0xd0
+#define	MV64XXX_I2C_STATUS_MAST_WR_ADDR_2_NO_ACK	0xd8
+#define	MV64XXX_I2C_STATUS_MAST_RD_ADDR_2_ACK		0xe0
+#define	MV64XXX_I2C_STATUS_MAST_RD_ADDR_2_NO_ACK	0xe8
+#define	MV64XXX_I2C_STATUS_NO_STATUS			0xf8
+
+/* Driver states */
+enum {
+	MV64XXX_I2C_STATE_INVALID,
+	MV64XXX_I2C_STATE_IDLE,
+	MV64XXX_I2C_STATE_WAITING_FOR_START_COND,
+	MV64XXX_I2C_STATE_WAITING_FOR_ADDR_1_ACK,
+	MV64XXX_I2C_STATE_WAITING_FOR_ADDR_2_ACK,
+	MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_ACK,
+	MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_DATA,
+	MV64XXX_I2C_STATE_ABORTING,
+};
+
+/* Driver actions */
+enum {
+	MV64XXX_I2C_ACTION_INVALID,
+	MV64XXX_I2C_ACTION_CONTINUE,
+	MV64XXX_I2C_ACTION_SEND_START,
+	MV64XXX_I2C_ACTION_SEND_ADDR_1,
+	MV64XXX_I2C_ACTION_SEND_ADDR_2,
+	MV64XXX_I2C_ACTION_SEND_DATA,
+	MV64XXX_I2C_ACTION_RCV_DATA,
+	MV64XXX_I2C_ACTION_RCV_DATA_STOP,
+	MV64XXX_I2C_ACTION_SEND_STOP,
+};
+
+struct mv64xxx_i2c_data {
+	int			irq;
+	uint			state;
+	uint			action;
+	u32			cntl_bits;
+	void			*reg_base;
+	ulong			reg_base_p;
+	u32			addr1;
+	u32			addr2;
+	uint			bytes_left;
+	uint			byte_posn;
+	uint			block;
+	int			rc;
+	u32			freq_m;
+	u32			freq_n;
+	wait_queue_head_t	waitq;
+	spinlock_t		lock;
+	struct i2c_msg		*msg;
+	struct i2c_adapter	adapter;
+};
+
+#endif /* I2C_MV64XXX_H */
diff -Nru a/include/linux/i2c-id.h b/include/linux/i2c-id.h
--- a/include/linux/i2c-id.h	2005-01-25 18:15:24 -07:00
+++ b/include/linux/i2c-id.h	2005-01-25 18:15:24 -07:00
@@ -200,6 +200,7 @@
 
 #define I2C_ALGO_SIBYTE 0x150000	/* Broadcom SiByte SOCs		*/
 #define I2C_ALGO_SGI	0x160000        /* SGI algorithm                */
+#define I2C_ALGO_MV64XXX 0x170000       /* Marvell mv64xxx i2c ctlr     */
 
 #define I2C_ALGO_EXP	0x800000	/* experimental			*/
 
@@ -304,5 +305,8 @@
 
 /* --- MCP107 adapter */
 #define I2C_HW_MPC107 0x00
+
+/* --- Marvell mv64xxx i2c adapter */
+#define I2C_HW_MV64XXX 0x00
 
 #endif /* LINUX_I2C_ID_H */
diff -Nru a/include/linux/mv643xx.h b/include/linux/mv643xx.h
--- a/include/linux/mv643xx.h	2005-01-25 18:15:24 -07:00
+++ b/include/linux/mv643xx.h	2005-01-25 18:15:24 -07:00
@@ -977,12 +977,9 @@
 /* I2C Registers                        */
 /****************************************/
 
-#define MV64340_I2C_SLAVE_ADDR                                      0xc000
-#define MV64340_I2C_EXTENDED_SLAVE_ADDR                             0xc010
-#define MV64340_I2C_DATA                                            0xc004
-#define MV64340_I2C_CONTROL                                         0xc008
-#define MV64340_I2C_STATUS_BAUDE_RATE                               0xc00C
-#define MV64340_I2C_SOFT_RESET                                      0xc01c
+#define	MV64XXX_I2C_CTLR_NAME					"mv64xxx i2c"
+#define MV64XXX_I2C_OFFSET					    0xc000
+#define MV64XXX_I2C_REG_BLOCK_SIZE				    0x0020
 
 /****************************************/
 /* GPP Interface Registers              */
@@ -1083,6 +1080,14 @@
 	u8	brg_can_tune;
 	u8	brg_clk_src;
 	u32	brg_clk_freq;
+};
+
+/* i2c Platform Device, Driver Data */
+struct mv64xxx_i2c_pdata {
+	u32	freq_m;
+	u32	freq_n;
+	u32	timeout;	/* In milliseconds */
+	u32	retries;
 };
 
 #endif /* __ASM_MV64340_H */

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

end of thread, other threads:[~2005-02-17 22:43 UTC | newest]

Thread overview: 23+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2005-02-08 23:27 [PATCH][I2C] Marvell mv64xxx i2c driver Mark A. Greer
2005-02-09  0:01 ` Bartlomiej Zolnierkiewicz
2005-02-09  0:32   ` Mark A. Greer
2005-02-09  1:24     ` Bartlomiej Zolnierkiewicz
2005-02-09 21:33       ` Mark A. Greer
2005-02-17 22:25         ` Greg KH
     [not found] <200502020315.14281.adobriyan@mail.ru>
2005-02-02  1:27 ` Greg KH
2005-02-02 17:26 ` Mark A. Greer
     [not found]   ` <200502031556.59319.adobriyan@mail.ru>
2005-02-03 19:12     ` Mark A. Greer
2005-02-04  0:38       ` Alexey Dobriyan
2005-02-04  0:04         ` Mark A. Greer
2005-02-04  9:45           ` Jean Delvare
2005-02-06 14:36             ` Jean Delvare
  -- strict thread matches above, loose matches on Subject: below --
2005-01-26  1:26 Mark A. Greer
2005-01-26 19:56 ` Jean Delvare
2005-01-26 20:33   ` Mark A. Greer
2005-01-26 21:56   ` Mark A. Greer
2005-01-26 22:42     ` Greg KH
2005-01-26 23:59       ` Mark A. Greer
2005-01-31 18:25 ` Greg KH
2005-01-31 18:41   ` Mark A. Greer
2005-02-01  0:46     ` Greg KH
2005-02-01 17:54       ` Mark A. Greer

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