* [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[parent not found: <1301582411-2437-1-git-send-email-kostap-eYqpPyKDWXRBDgjK7y7TUQ@public.gmane.org>]
* [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).