linux-i2c.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] Fix I2C combined access on Marvell mv64xxxx
@ 2011-03-31 14:40 Konstantin Porotchkin
       [not found] ` <1301582411-2437-1-git-send-email-kostap-eYqpPyKDWXRBDgjK7y7TUQ@public.gmane.org>
  0 siblings, 1 reply; 6+ messages in thread
From: Konstantin Porotchkin @ 2011-03-31 14:40 UTC (permalink / raw)
  To: linux-i2c-u79uwXL29TY76Z2rM5mHXA; +Cc: Konstantin Porotchkin

Eliminate erroneus data send in combined access mode when INT
is cleared in i2c controller. Instead disable INT delivery to CPU
and re-enable it on the next START.

Signed-off-by: Konstantin Porotchkin <kostap-eYqpPyKDWXRBDgjK7y7TUQ@public.gmane.org>
---
 drivers/i2c/busses/i2c-mv64xxx.c |   19 +++++++++++++++----
 1 files changed, 15 insertions(+), 4 deletions(-)

diff --git a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c
index a9941c6..d013e25 100644
--- a/drivers/i2c/busses/i2c-mv64xxx.c
+++ b/drivers/i2c/busses/i2c-mv64xxx.c
@@ -15,6 +15,7 @@
 #include <linux/spinlock.h>
 #include <linux/i2c.h>
 #include <linux/interrupt.h>
+#include <linux/delay.h>
 #include <linux/mv643xx_i2c.h>
 #include <linux/platform_device.h>
 #include <linux/io.h>
@@ -98,6 +99,7 @@ struct mv64xxx_i2c_data {
 	int			rc;
 	u32			freq_m;
 	u32			freq_n;
+	int			irq_disabled;
 	wait_queue_head_t	waitq;
 	spinlock_t		lock;
 	struct i2c_msg		*msg;
@@ -239,10 +241,11 @@ mv64xxx_i2c_do_action(struct mv64xxx_i2c_data *drv_data)
 {
 	switch(drv_data->action) {
 	case MV64XXX_I2C_ACTION_SEND_RESTART:
-		drv_data->cntl_bits |= MV64XXX_I2C_REG_CONTROL_START;
-		drv_data->cntl_bits &= ~MV64XXX_I2C_REG_CONTROL_INTEN;
-		writel(drv_data->cntl_bits,
-			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+		/* can't mask interrupts by clearing the INTEN as this
+		 * triggers the controller to send the data.
+		 */
+		drv_data->irq_disabled = 1;
+		disable_irq_nosync(drv_data->irq);
 		drv_data->block = 0;
 		wake_up_interruptible(&drv_data->waitq);
 		break;
@@ -255,6 +258,11 @@ mv64xxx_i2c_do_action(struct mv64xxx_i2c_data *drv_data)
 	case MV64XXX_I2C_ACTION_SEND_START:
 		writel(drv_data->cntl_bits | MV64XXX_I2C_REG_CONTROL_START,
 			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+		if (drv_data->irq_disabled) {
+			udelay(3);
+			drv_data->irq_disabled = 0;
+			enable_irq(drv_data->irq);
+		}
 		break;
 
 	case MV64XXX_I2C_ACTION_SEND_ADDR_1:
@@ -327,6 +335,8 @@ mv64xxx_i2c_intr(int irq, void *dev_id)
 		mv64xxx_i2c_fsm(drv_data, status);
 		mv64xxx_i2c_do_action(drv_data);
 		rc = IRQ_HANDLED;
+		if (drv_data->state == MV64XXX_I2C_STATE_WAITING_FOR_RESTART)
+			break;
 	}
 	spin_unlock_irqrestore(&drv_data->lock, flags);
 
@@ -553,6 +563,7 @@ mv64xxx_i2c_probe(struct platform_device *pd)
 		rc = -ENXIO;
 		goto exit_unmap_regs;
 	}
+	drv_data->irq_disabled = 0;
 	drv_data->adapter.dev.parent = &pd->dev;
 	drv_data->adapter.algo = &mv64xxx_i2c_algo;
 	drv_data->adapter.owner = THIS_MODULE;
-- 
1.7.4.1

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

* [PATCH] Add-delay-after-setting-the-STOP-bit-i2c-mv64xxx
       [not found] ` <1301582411-2437-1-git-send-email-kostap-eYqpPyKDWXRBDgjK7y7TUQ@public.gmane.org>
@ 2011-03-31 14:40   ` Konstantin Porotchkin
  2011-03-31 14:40   ` [PATCH] Dove - Support init val. for i2c delay after STOP Konstantin Porotchkin
                     ` (3 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Konstantin Porotchkin @ 2011-03-31 14:40 UTC (permalink / raw)
  To: linux-i2c-u79uwXL29TY76Z2rM5mHXA; +Cc: Konstantin Porotchkin

Add delay after setting the STOP bit in i2c control register

Signed-off-by: Konstantin Porotchkin <kostap-eYqpPyKDWXRBDgjK7y7TUQ@public.gmane.org>
---
 drivers/i2c/busses/i2c-mv64xxx.c |   25 +++++++++++++++++++++++++
 include/linux/mv643xx_i2c.h      |    1 +
 2 files changed, 26 insertions(+), 0 deletions(-)

diff --git a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c
index d013e25..a6e9a3f 100644
--- a/drivers/i2c/busses/i2c-mv64xxx.c
+++ b/drivers/i2c/busses/i2c-mv64xxx.c
@@ -99,6 +99,7 @@ struct mv64xxx_i2c_data {
 	int			rc;
 	u32			freq_m;
 	u32			freq_n;
+	u32			delay_after_stop;
 	int			irq_disabled;
 	wait_queue_head_t	waitq;
 	spinlock_t		lock;
@@ -114,6 +115,25 @@ struct mv64xxx_i2c_data {
  *****************************************************************************
  */
 
+static void
+mv64xxx_i2c_wait_after_stop(struct mv64xxx_i2c_data *drv_data)
+{
+	int i = 0;
+
+	udelay(drv_data->delay_after_stop);
+
+	/* wait for the stop bit up to 100 usec more */
+	while (readl(drv_data->reg_base + MV64XXX_I2C_REG_CONTROL) &
+	       MV64XXX_I2C_REG_CONTROL_STOP){
+		udelay(1);
+		if (i++ > 100) {
+			dev_err(&drv_data->adapter.dev,
+				" I2C bus locked, stop bit not cleared\n");
+			break;
+		}
+	}
+
+}
 /* Reset hardware and initialize FSM */
 static void
 mv64xxx_i2c_hw_init(struct mv64xxx_i2c_data *drv_data)
@@ -125,6 +145,7 @@ mv64xxx_i2c_hw_init(struct mv64xxx_i2c_data *drv_data)
 	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);
+	mv64xxx_i2c_wait_after_stop(drv_data);
 	drv_data->state = MV64XXX_I2C_STATE_IDLE;
 }
 
@@ -299,6 +320,7 @@ mv64xxx_i2c_do_action(struct mv64xxx_i2c_data *drv_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);
+		mv64xxx_i2c_wait_after_stop(drv_data);
 		drv_data->block = 0;
 		wake_up_interruptible(&drv_data->waitq);
 		break;
@@ -314,6 +336,7 @@ mv64xxx_i2c_do_action(struct mv64xxx_i2c_data *drv_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);
+		mv64xxx_i2c_wait_after_stop(drv_data);
 		drv_data->block = 0;
 		wake_up_interruptible(&drv_data->waitq);
 		break;
@@ -558,6 +581,8 @@ mv64xxx_i2c_probe(struct platform_device *pd)
 
 	drv_data->freq_m = pdata->freq_m;
 	drv_data->freq_n = pdata->freq_n;
+	drv_data->delay_after_stop = pdata->delay_after_stop ?
+		pdata->delay_after_stop : 10;
 	drv_data->irq = platform_get_irq(pd, 0);
 	if (drv_data->irq < 0) {
 		rc = -ENXIO;
diff --git a/include/linux/mv643xx_i2c.h b/include/linux/mv643xx_i2c.h
index 5db5152..6d151c4 100644
--- a/include/linux/mv643xx_i2c.h
+++ b/include/linux/mv643xx_i2c.h
@@ -16,6 +16,7 @@
 struct mv64xxx_i2c_pdata {
 	u32	freq_m;
 	u32	freq_n;
+	u32	delay_after_stop;
 	u32	timeout;	/* In milliseconds */
 };
 
-- 
1.7.4.1

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

* [PATCH] Dove - Support init val. for i2c delay after STOP
       [not found] ` <1301582411-2437-1-git-send-email-kostap-eYqpPyKDWXRBDgjK7y7TUQ@public.gmane.org>
  2011-03-31 14:40   ` [PATCH] Add-delay-after-setting-the-STOP-bit-i2c-mv64xxx Konstantin Porotchkin
@ 2011-03-31 14:40   ` Konstantin Porotchkin
  2011-03-31 14:40   ` [PATCH] Support power management in i2c mv64xx driver Konstantin Porotchkin
                     ` (2 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Konstantin Porotchkin @ 2011-03-31 14:40 UTC (permalink / raw)
  To: linux-i2c-u79uwXL29TY76Z2rM5mHXA; +Cc: Konstantin Porotchkin

Add initialization value for delay after STOP bit for Dove i2c
controller (mv-64xxx driver).

Signed-off-by: Konstantin Porotchkin <kostap-eYqpPyKDWXRBDgjK7y7TUQ@public.gmane.org>
---
 arch/arm/mach-dove/common.c |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-dove/common.c b/arch/arm/mach-dove/common.c
index e06a88f..7ebc82f 100644
--- a/arch/arm/mach-dove/common.c
+++ b/arch/arm/mach-dove/common.c
@@ -497,6 +497,7 @@ void __init dove_spi1_init(void)
 static struct mv64xxx_i2c_pdata dove_i2c_data = {
 	.freq_m		= 10, /* assumes 166 MHz TCLK gets 94.3kHz */
 	.freq_n		= 3,
+	.delay_after_stop = 3, /* 3 ms delay needed when freq is 94.3kHz */
 	.timeout	= 1000, /* Default timeout of 1 second */
 };
 
-- 
1.7.4.1

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

* [PATCH] Support power management in i2c mv64xx driver
       [not found] ` <1301582411-2437-1-git-send-email-kostap-eYqpPyKDWXRBDgjK7y7TUQ@public.gmane.org>
  2011-03-31 14:40   ` [PATCH] Add-delay-after-setting-the-STOP-bit-i2c-mv64xxx Konstantin Porotchkin
  2011-03-31 14:40   ` [PATCH] Dove - Support init val. for i2c delay after STOP Konstantin Porotchkin
@ 2011-03-31 14:40   ` Konstantin Porotchkin
  2011-03-31 14:40   ` [PATCH] Support i2c expander on mv64xxx adapter Konstantin Porotchkin
  2011-05-02 14:53   ` [PATCH] Fix I2C combined access on Marvell mv64xxxx Konstantin Porotchkin
  4 siblings, 0 replies; 6+ messages in thread
From: Konstantin Porotchkin @ 2011-03-31 14:40 UTC (permalink / raw)
  To: linux-i2c-u79uwXL29TY76Z2rM5mHXA; +Cc: Konstantin Porotchkin

Added resume operation for PM support

Signed-off-by: Konstantin Porotchkin <kostap-eYqpPyKDWXRBDgjK7y7TUQ@public.gmane.org>
---
 drivers/i2c/busses/i2c-mv64xxx.c |   14 ++++++++++++++
 1 files changed, 14 insertions(+), 0 deletions(-)

diff --git a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c
index a6e9a3f..b246dab 100644
--- a/drivers/i2c/busses/i2c-mv64xxx.c
+++ b/drivers/i2c/busses/i2c-mv64xxx.c
@@ -638,9 +638,23 @@ mv64xxx_i2c_remove(struct platform_device *dev)
 	return rc;
 }
 
+#ifdef CONFIG_PM
+static int
+mv64xxx_i2c_resume(struct platform_device *dev)
+{
+	struct mv64xxx_i2c_data	*drv_data = platform_get_drvdata(dev);
+	mv64xxx_i2c_hw_init(drv_data);
+	return 0;
+}
+
+#else
+#define mv64xxx_i2c_resume NULL
+#endif
+
 static struct platform_driver mv64xxx_i2c_driver = {
 	.probe	= mv64xxx_i2c_probe,
 	.remove	= __devexit_p(mv64xxx_i2c_remove),
+	.resume = mv64xxx_i2c_resume,
 	.driver	= {
 		.owner	= THIS_MODULE,
 		.name	= MV64XXX_I2C_CTLR_NAME,
-- 
1.7.4.1

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

* [PATCH] Support i2c expander on mv64xxx adapter
       [not found] ` <1301582411-2437-1-git-send-email-kostap-eYqpPyKDWXRBDgjK7y7TUQ@public.gmane.org>
                     ` (2 preceding siblings ...)
  2011-03-31 14:40   ` [PATCH] Support power management in i2c mv64xx driver Konstantin Porotchkin
@ 2011-03-31 14:40   ` Konstantin Porotchkin
  2011-05-02 14:53   ` [PATCH] Fix I2C combined access on Marvell mv64xxxx Konstantin Porotchkin
  4 siblings, 0 replies; 6+ messages in thread
From: Konstantin Porotchkin @ 2011-03-31 14:40 UTC (permalink / raw)
  To: linux-i2c-u79uwXL29TY76Z2rM5mHXA; +Cc: Konstantin Porotchkin

Support i2c bus expander on Marvell mv64xxx platforms

Signed-off-by: Konstantin Porotchkin <kostap-eYqpPyKDWXRBDgjK7y7TUQ@public.gmane.org>
---
 drivers/i2c/busses/Kconfig       |    7 ++
 drivers/i2c/busses/i2c-mv64xxx.c |  185 ++++++++++++++++++++++++++++++++------
 include/linux/mv643xx_i2c.h      |    8 ++
 3 files changed, 174 insertions(+), 26 deletions(-)

diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 326652f..d125142 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -453,6 +453,13 @@ config I2C_MV64XXX
 	  This driver can also be built as a module.  If so, the module
 	  will be called i2c-mv64xxx.
 
+config I2C_MV64XXX_PORT_EXPANDER
+	bool "Marvell mv64xxx I2C Port Expander"
+	depends on I2C_MV64XXX
+	help
+	  If you say yes to this option, support will be included for the
+	  I2C port expander on some of Marvell SoC's.
+
 config I2C_MXS
 	tristate "Freescale i.MX28 I2C interface"
 	depends on SOC_IMX28
diff --git a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c
index b246dab..bc69506 100644
--- a/drivers/i2c/busses/i2c-mv64xxx.c
+++ b/drivers/i2c/busses/i2c-mv64xxx.c
@@ -19,6 +19,7 @@
 #include <linux/mv643xx_i2c.h>
 #include <linux/platform_device.h>
 #include <linux/io.h>
+#include <linux/semaphore.h>
 
 /* Register defines */
 #define	MV64XXX_I2C_REG_SLAVE_ADDR			0x00
@@ -104,6 +105,15 @@ struct mv64xxx_i2c_data {
 	wait_queue_head_t	waitq;
 	spinlock_t		lock;
 	struct i2c_msg		*msg;
+	struct i2c_adapter	*adapter;
+#ifdef CONFIG_I2C_MV64XXX_PORT_EXPANDER
+	struct semaphore	exp_sem;
+	int	(*select_exp_port)(unsigned int port_id);
+#endif
+};
+
+struct mv64xxx_i2c_exp_data {
+	struct mv64xxx_i2c_data	*hw_adapter;
 	struct i2c_adapter	adapter;
 };
 
@@ -127,7 +137,7 @@ mv64xxx_i2c_wait_after_stop(struct mv64xxx_i2c_data *drv_data)
 	       MV64XXX_I2C_REG_CONTROL_STOP){
 		udelay(1);
 		if (i++ > 100) {
-			dev_err(&drv_data->adapter.dev,
+			dev_err(&drv_data->adapter->dev,
 				" I2C bus locked, stop bit not cleared\n");
 			break;
 		}
@@ -246,7 +256,7 @@ mv64xxx_i2c_fsm(struct mv64xxx_i2c_data *drv_data, u32 status)
 		break;
 
 	default:
-		dev_err(&drv_data->adapter.dev,
+		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,
@@ -327,7 +337,7 @@ mv64xxx_i2c_do_action(struct mv64xxx_i2c_data *drv_data)
 
 	case MV64XXX_I2C_ACTION_INVALID:
 	default:
-		dev_err(&drv_data->adapter.dev,
+		dev_err(&drv_data->adapter->dev,
 			"mv64xxx_i2c_do_action: Invalid action: %d\n",
 			drv_data->action);
 		drv_data->rc = -EIO;
@@ -407,7 +417,7 @@ mv64xxx_i2c_wait_for_completion(struct mv64xxx_i2c_data *drv_data)
 	char		abort = 0;
 
 	time_left = wait_event_interruptible_timeout(drv_data->waitq,
-		!drv_data->block, drv_data->adapter.timeout);
+		!drv_data->block, drv_data->adapter->timeout);
 
 	spin_lock_irqsave(&drv_data->lock, flags);
 	if (!time_left) { /* Timed out */
@@ -423,11 +433,11 @@ mv64xxx_i2c_wait_for_completion(struct mv64xxx_i2c_data *drv_data)
 		spin_unlock_irqrestore(&drv_data->lock, flags);
 
 		time_left = wait_event_timeout(drv_data->waitq,
-			!drv_data->block, drv_data->adapter.timeout);
+			!drv_data->block, drv_data->adapter->timeout);
 
 		if ((time_left <= 0) && drv_data->block) {
 			drv_data->state = MV64XXX_I2C_STATE_IDLE;
-			dev_err(&drv_data->adapter.dev,
+			dev_err(&drv_data->adapter->dev,
 				"mv64xxx: I2C bus locked, block: %d, "
 				"time_left: %d\n", drv_data->block,
 				(int)time_left);
@@ -491,7 +501,7 @@ mv64xxx_i2c_functionality(struct i2c_adapter *adap)
 {
 	return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR | I2C_FUNC_SMBUS_EMUL;
 }
-
+#ifndef CONFIG_I2C_MV64XXX_PORT_EXPANDER
 static int
 mv64xxx_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
 {
@@ -512,7 +522,7 @@ static const struct i2c_algorithm mv64xxx_i2c_algo = {
 	.master_xfer = mv64xxx_i2c_xfer,
 	.functionality = mv64xxx_i2c_functionality,
 };
-
+#endif
 /*
  *****************************************************************************
  *
@@ -532,7 +542,7 @@ mv64xxx_i2c_map_regs(struct platform_device *pd,
 
 	size = resource_size(r);
 
-	if (!request_mem_region(r->start, size, drv_data->adapter.name))
+	if (!request_mem_region(r->start, size, drv_data->adapter->name))
 		return -EBUSY;
 
 	drv_data->reg_base = ioremap(r->start, size);
@@ -559,6 +569,9 @@ mv64xxx_i2c_probe(struct platform_device *pd)
 {
 	struct mv64xxx_i2c_data		*drv_data;
 	struct mv64xxx_i2c_pdata	*pdata = pd->dev.platform_data;
+#ifndef CONFIG_I2C_MV64XXX_PORT_EXPANDER
+	struct i2c_adapter	*adapter;
+#endif
 	int	rc;
 
 	if ((pd->id != 0) || !pdata)
@@ -567,18 +580,30 @@ mv64xxx_i2c_probe(struct platform_device *pd)
 	drv_data = kzalloc(sizeof(struct mv64xxx_i2c_data), GFP_KERNEL);
 	if (!drv_data)
 		return -ENOMEM;
+#ifndef CONFIG_I2C_MV64XXX_PORT_EXPANDER
+	adapter = kzalloc(sizeof(struct i2c_adapter), GFP_KERNEL);
+	if (!adapter)
+		return -ENOMEM;
 
+	drv_data->adapter = adapter;
+#endif
 	if (mv64xxx_i2c_map_regs(pd, drv_data)) {
 		rc = -ENODEV;
 		goto exit_kfree;
 	}
 
-	strlcpy(drv_data->adapter.name, MV64XXX_I2C_CTLR_NAME " adapter",
-		sizeof(drv_data->adapter.name));
-
 	init_waitqueue_head(&drv_data->waitq);
 	spin_lock_init(&drv_data->lock);
-
+#ifdef CONFIG_I2C_MV64XXX_PORT_EXPANDER
+	sema_init(&drv_data->exp_sem, 1);
+	drv_data->select_exp_port = pdata->select_exp_port;
+	if (drv_data->select_exp_port == 0) {
+		dev_err(&drv_data->adapter->dev,
+			"mv64xxx expander: Invalid i2c port selector\n");
+		rc = -EINVAL;
+		goto exit_unmap_regs;
+	}
+#endif
 	drv_data->freq_m = pdata->freq_m;
 	drv_data->freq_n = pdata->freq_n;
 	drv_data->delay_after_stop = pdata->delay_after_stop ?
@@ -589,34 +614,45 @@ mv64xxx_i2c_probe(struct platform_device *pd)
 		goto exit_unmap_regs;
 	}
 	drv_data->irq_disabled = 0;
-	drv_data->adapter.dev.parent = &pd->dev;
-	drv_data->adapter.algo = &mv64xxx_i2c_algo;
-	drv_data->adapter.owner = THIS_MODULE;
-	drv_data->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
-	drv_data->adapter.timeout = msecs_to_jiffies(pdata->timeout);
-	drv_data->adapter.nr = pd->id;
+#ifndef CONFIG_I2C_MV64XXX_PORT_EXPANDER
+	strlcpy(drv_data->adapter->name, MV64XXX_I2C_CTLR_NAME " adapter",
+		sizeof(drv_data->adapter->name));
+
+	drv_data->adapter->dev.parent = &pd->dev;
+	drv_data->adapter->algo = &mv64xxx_i2c_algo;
+	drv_data->adapter->owner = THIS_MODULE;
+	drv_data->adapter->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
+	drv_data->adapter->timeout = msecs_to_jiffies(pdata->timeout);
+	drv_data->adapter->nr = pd->id;
+	i2c_set_adapdata(drv_data->adapter, drv_data);
+#endif
 	platform_set_drvdata(pd, drv_data);
-	i2c_set_adapdata(&drv_data->adapter, drv_data);
 
 	mv64xxx_i2c_hw_init(drv_data);
 
 	if (request_irq(drv_data->irq, mv64xxx_i2c_intr, 0,
 			MV64XXX_I2C_CTLR_NAME, drv_data)) {
-		dev_err(&drv_data->adapter.dev,
+		dev_err(&drv_data->adapter->dev,
 			"mv64xxx: Can't register intr handler irq: %d\n",
 			drv_data->irq);
 		rc = -EINVAL;
 		goto exit_unmap_regs;
-	} else if ((rc = i2c_add_numbered_adapter(&drv_data->adapter)) != 0) {
-		dev_err(&drv_data->adapter.dev,
+#ifndef CONFIG_I2C_MV64XXX_PORT_EXPANDER
+	}
+	rc = i2c_add_numbered_adapter(drv_data->adapter);
+	if (rc != 0) {
+		dev_err(&drv_data->adapter->dev,
 			"mv64xxx: Can't add i2c adapter, rc: %d\n", -rc);
 		goto exit_free_irq;
+#endif
 	}
 
 	return 0;
 
+#ifndef CONFIG_I2C_MV64XXX_PORT_EXPANDER
 	exit_free_irq:
 		free_irq(drv_data->irq, drv_data);
+#endif
 	exit_unmap_regs:
 		mv64xxx_i2c_unmap_regs(drv_data);
 	exit_kfree:
@@ -628,9 +664,11 @@ static int __devexit
 mv64xxx_i2c_remove(struct platform_device *dev)
 {
 	struct mv64xxx_i2c_data		*drv_data = platform_get_drvdata(dev);
-	int	rc;
+	int	rc = 0;
 
-	rc = i2c_del_adapter(&drv_data->adapter);
+#ifndef CONFIG_I2C_MV64XXX_PORT_EXPANDER
+	rc = i2c_del_adapter(drv_data->adapter);
+#endif
 	free_irq(drv_data->irq, drv_data);
 	mv64xxx_i2c_unmap_regs(drv_data);
 	kfree(drv_data);
@@ -661,15 +699,110 @@ static struct platform_driver mv64xxx_i2c_driver = {
 	},
 };
 
+#ifdef CONFIG_I2C_MV64XXX_PORT_EXPANDER
+static int
+mv64xxx_i2c_exp_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
+{
+	struct mv64xxx_i2c_exp_data *exp_drv_data = i2c_get_adapdata(adap);
+	struct mv64xxx_i2c_data *drv_data = exp_drv_data->hw_adapter;
+	int	i, rc;
+
+	down(&drv_data->exp_sem);
+	drv_data->adapter = adap;
+	drv_data->select_exp_port(adap->nr);
+	for (i = 0; i < num; i++) {
+		rc = mv64xxx_i2c_execute_msg(drv_data, &msgs[i],
+						i == 0, i + 1 == num);
+		if (rc < 0) {
+			up(&drv_data->exp_sem);
+			return rc;
+		}
+	}
+	up(&drv_data->exp_sem);
+	return num;
+}
+
+static const struct i2c_algorithm mv64xxx_i2c_exp_algo = {
+	.master_xfer = mv64xxx_i2c_exp_xfer,
+	.functionality = mv64xxx_i2c_functionality,
+};
+
+static int __devinit
+mv64xxx_i2c_exp_probe(struct platform_device *pd)
+{
+	struct mv64xxx_i2c_exp_data	*exp_drv_data;
+	struct mv64xxx_i2c_exp_pdata	*pdata = pd->dev.platform_data;
+	int	rc = 0;
+
+	if (!pdata)
+		return -ENODEV;
+
+	exp_drv_data = devm_kzalloc(&pd->dev,
+				sizeof(struct mv64xxx_i2c_exp_data),
+				GFP_KERNEL);
+	if (!exp_drv_data)
+		return -ENOMEM;
+
+	strlcpy(exp_drv_data->adapter.name,
+		MV64XXX_I2C_EXPANDER_NAME " adapter",
+		sizeof(exp_drv_data->adapter.name));
+	exp_drv_data->adapter.dev.parent = &pd->dev;
+	exp_drv_data->adapter.algo = &mv64xxx_i2c_exp_algo;
+	exp_drv_data->adapter.owner = THIS_MODULE;
+	exp_drv_data->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
+	exp_drv_data->adapter.timeout = pdata->timeout;
+	exp_drv_data->adapter.nr = pd->id;
+	exp_drv_data->hw_adapter = platform_get_drvdata(pdata->hw_adapter);
+	platform_set_drvdata(pd, exp_drv_data);
+	i2c_set_adapdata(&exp_drv_data->adapter, exp_drv_data);
+
+	rc = i2c_add_numbered_adapter(&exp_drv_data->adapter);
+	if (rc != 0) {
+		dev_err(&pd->dev,
+			"mv64xxx expander: Can't add i2c adapter, rc: %d\n",
+			-rc);
+	}
+	return rc;
+}
+
+static int __devexit
+mv64xxx_i2c_exp_remove(struct platform_device *dev)
+{
+	struct mv64xxx_i2c_exp_data	*exp_drv_data =
+						platform_get_drvdata(dev);
+
+	return i2c_del_adapter(&exp_drv_data->adapter);
+}
+
+static struct platform_driver mv64xxx_i2c_exp_driver = {
+	.probe	= mv64xxx_i2c_exp_probe,
+	.remove	= __devexit_p(mv64xxx_i2c_exp_remove),
+	.driver	= {
+		.owner	= THIS_MODULE,
+		.name	= MV64XXX_I2C_EXPANDER_NAME,
+	},
+};
+#endif
+
 static int __init
 mv64xxx_i2c_init(void)
 {
-	return platform_driver_register(&mv64xxx_i2c_driver);
+	int rc = platform_driver_register(&mv64xxx_i2c_driver);
+	if (rc < 0)
+		return rc;
+
+#ifdef CONFIG_I2C_MV64XXX_PORT_EXPANDER
+	rc = platform_driver_register(&mv64xxx_i2c_exp_driver);
+#endif
+	return rc;
 }
 
 static void __exit
 mv64xxx_i2c_exit(void)
 {
+#ifdef CONFIG_I2C_MV64XXX_PORT_EXPANDER
+	platform_driver_unregister(&mv64xxx_i2c_exp_driver);
+#endif
 	platform_driver_unregister(&mv64xxx_i2c_driver);
 }
 
diff --git a/include/linux/mv643xx_i2c.h b/include/linux/mv643xx_i2c.h
index 6d151c4..559c839 100644
--- a/include/linux/mv643xx_i2c.h
+++ b/include/linux/mv643xx_i2c.h
@@ -11,6 +11,7 @@
 #include <linux/types.h>
 
 #define MV64XXX_I2C_CTLR_NAME	"mv64xxx_i2c"
+#define MV64XXX_I2C_EXPANDER_NAME	"mv64xxx_i2c_exp"
 
 /* i2c Platform Device, Driver Data */
 struct mv64xxx_i2c_pdata {
@@ -18,6 +19,13 @@ struct mv64xxx_i2c_pdata {
 	u32	freq_n;
 	u32	delay_after_stop;
 	u32	timeout;	/* In milliseconds */
+	int     (*select_exp_port)	(unsigned int port_id);
+};
+
+/* i2c expander driver data */
+struct mv64xxx_i2c_exp_pdata {
+	struct platform_device  *hw_adapter;
+	u32	timeout;	/* In milliseconds */
 };
 
 #endif /*_MV64XXX_I2C_H_*/
-- 
1.7.4.1

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

* Re: [PATCH] Fix I2C combined access on Marvell mv64xxxx
       [not found] ` <1301582411-2437-1-git-send-email-kostap-eYqpPyKDWXRBDgjK7y7TUQ@public.gmane.org>
                     ` (3 preceding siblings ...)
  2011-03-31 14:40   ` [PATCH] Support i2c expander on mv64xxx adapter Konstantin Porotchkin
@ 2011-05-02 14:53   ` Konstantin Porotchkin
  4 siblings, 0 replies; 6+ messages in thread
From: Konstantin Porotchkin @ 2011-05-02 14:53 UTC (permalink / raw)
  To: ben-linux-elnMNo+KYs3YtjvyW6yDsg; +Cc: linux-i2c-u79uwXL29TY76Z2rM5mHXA

Hello, Ben,

Are you going to integrate Marvel's i2c patches dated March 31 to the
kernel tree?

Thank you for your help
Konstantin

On Thu, Mar 31, 2011 at 5:40 PM, Konstantin Porotchkin
<kostap-eYqpPyKDWXRBDgjK7y7TUQ@public.gmane.org> wrote:
> Eliminate erroneus data send in combined access mode when INT
> is cleared in i2c controller. Instead disable INT delivery to CPU
> and re-enable it on the next START.
>
> Signed-off-by: Konstantin Porotchkin <kostap-eYqpPyKDWXRBDgjK7y7TUQ@public.gmane.org>
> ---
>  drivers/i2c/busses/i2c-mv64xxx.c |   19 +++++++++++++++----
>  1 files changed, 15 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c
> index a9941c6..d013e25 100644
> --- a/drivers/i2c/busses/i2c-mv64xxx.c
> +++ b/drivers/i2c/busses/i2c-mv64xxx.c
> @@ -15,6 +15,7 @@
>  #include <linux/spinlock.h>
>  #include <linux/i2c.h>
>  #include <linux/interrupt.h>
> +#include <linux/delay.h>
>  #include <linux/mv643xx_i2c.h>
>  #include <linux/platform_device.h>
>  #include <linux/io.h>
> @@ -98,6 +99,7 @@ struct mv64xxx_i2c_data {
>        int                     rc;
>        u32                     freq_m;
>        u32                     freq_n;
> +       int                     irq_disabled;
>        wait_queue_head_t       waitq;
>        spinlock_t              lock;
>        struct i2c_msg          *msg;
> @@ -239,10 +241,11 @@ mv64xxx_i2c_do_action(struct mv64xxx_i2c_data *drv_data)
>  {
>        switch(drv_data->action) {
>        case MV64XXX_I2C_ACTION_SEND_RESTART:
> -               drv_data->cntl_bits |= MV64XXX_I2C_REG_CONTROL_START;
> -               drv_data->cntl_bits &= ~MV64XXX_I2C_REG_CONTROL_INTEN;
> -               writel(drv_data->cntl_bits,
> -                       drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
> +               /* can't mask interrupts by clearing the INTEN as this
> +                * triggers the controller to send the data.
> +                */
> +               drv_data->irq_disabled = 1;
> +               disable_irq_nosync(drv_data->irq);
>                drv_data->block = 0;
>                wake_up_interruptible(&drv_data->waitq);
>                break;
> @@ -255,6 +258,11 @@ mv64xxx_i2c_do_action(struct mv64xxx_i2c_data *drv_data)
>        case MV64XXX_I2C_ACTION_SEND_START:
>                writel(drv_data->cntl_bits | MV64XXX_I2C_REG_CONTROL_START,
>                        drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
> +               if (drv_data->irq_disabled) {
> +                       udelay(3);
> +                       drv_data->irq_disabled = 0;
> +                       enable_irq(drv_data->irq);
> +               }
>                break;
>
>        case MV64XXX_I2C_ACTION_SEND_ADDR_1:
> @@ -327,6 +335,8 @@ mv64xxx_i2c_intr(int irq, void *dev_id)
>                mv64xxx_i2c_fsm(drv_data, status);
>                mv64xxx_i2c_do_action(drv_data);
>                rc = IRQ_HANDLED;
> +               if (drv_data->state == MV64XXX_I2C_STATE_WAITING_FOR_RESTART)
> +                       break;
>        }
>        spin_unlock_irqrestore(&drv_data->lock, flags);
>
> @@ -553,6 +563,7 @@ mv64xxx_i2c_probe(struct platform_device *pd)
>                rc = -ENXIO;
>                goto exit_unmap_regs;
>        }
> +       drv_data->irq_disabled = 0;
>        drv_data->adapter.dev.parent = &pd->dev;
>        drv_data->adapter.algo = &mv64xxx_i2c_algo;
>        drv_data->adapter.owner = THIS_MODULE;
> --
> 1.7.4.1
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-i2c" in
> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>

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

end of thread, other threads:[~2011-05-02 14:53 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-03-31 14:40 [PATCH] Fix I2C combined access on Marvell mv64xxxx Konstantin Porotchkin
     [not found] ` <1301582411-2437-1-git-send-email-kostap-eYqpPyKDWXRBDgjK7y7TUQ@public.gmane.org>
2011-03-31 14:40   ` [PATCH] Add-delay-after-setting-the-STOP-bit-i2c-mv64xxx Konstantin Porotchkin
2011-03-31 14:40   ` [PATCH] Dove - Support init val. for i2c delay after STOP Konstantin Porotchkin
2011-03-31 14:40   ` [PATCH] Support power management in i2c mv64xx driver Konstantin Porotchkin
2011-03-31 14:40   ` [PATCH] Support i2c expander on mv64xxx adapter Konstantin Porotchkin
2011-05-02 14:53   ` [PATCH] Fix I2C combined access on Marvell mv64xxxx Konstantin Porotchkin

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