Browse files

Added combined access to i2c controller

Adds combined access to i2C controller (i2c-mv64xxx.c).

Signed-off-by: Rabeeh Khoury <rabeeh@solid-run.com>
  • Loading branch information...
1 parent 4f450ff commit 6a661ac638aa65423b95993248dd481eefd5d5b3 @rabeeh committed Dec 10, 2012
Showing with 112 additions and 53 deletions.
  1. +111 −53 drivers/i2c/busses/i2c-mv64xxx.c
  2. +1 −0 include/linux/mv643xx_i2c.h
View
164 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>
@@ -64,25 +65,25 @@ enum {
MV64XXX_I2C_STATE_INVALID,
MV64XXX_I2C_STATE_IDLE,
MV64XXX_I2C_STATE_WAITING_FOR_START_COND,
- MV64XXX_I2C_STATE_WAITING_FOR_RESTART,
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_WAITING_FOR_REPEATED_START,
};
/* Driver actions */
enum {
MV64XXX_I2C_ACTION_INVALID,
MV64XXX_I2C_ACTION_CONTINUE,
MV64XXX_I2C_ACTION_SEND_START,
- MV64XXX_I2C_ACTION_SEND_RESTART,
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,
+ MV64XXX_I2C_ACTION_NO_STOP,
};
struct mv64xxx_i2c_data {
@@ -103,13 +104,17 @@ struct mv64xxx_i2c_data {
int rc;
u32 freq_m;
u32 freq_n;
+ u32 delay_after_stop;
+ int irq_disabled;
#if defined(CONFIG_HAVE_CLK)
struct clk *clk;
#endif
wait_queue_head_t waitq;
spinlock_t lock;
+ struct device *dev;
struct i2c_msg *msg;
struct i2c_adapter adapter;
+ u32 combine_access;
};
/*
@@ -120,6 +125,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->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)
@@ -131,6 +155,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;
}
@@ -167,18 +192,15 @@ mv64xxx_i2c_fsm(struct mv64xxx_i2c_data *drv_data, u32 status)
/* 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->aborting
- && (drv_data->byte_posn != 0))) {
- if (drv_data->send_stop) {
- drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
- drv_data->state = MV64XXX_I2C_STATE_IDLE;
- } else {
- drv_data->action =
- MV64XXX_I2C_ACTION_SEND_RESTART;
- drv_data->state =
- MV64XXX_I2C_STATE_WAITING_FOR_RESTART;
- }
+ if (drv_data->bytes_left == 0) {
+ if ((drv_data->aborting
+ && (drv_data->byte_posn != 0)) || !drv_data->combine_access) {
+ drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
+ drv_data->state = MV64XXX_I2C_STATE_IDLE;
+ } else {
+ drv_data->action = MV64XXX_I2C_ACTION_NO_STOP;
+ drv_data->state = MV64XXX_I2C_STATE_WAITING_FOR_REPEATED_START;
+ }
} else {
drv_data->action = MV64XXX_I2C_ACTION_SEND_DATA;
drv_data->state =
@@ -231,7 +253,7 @@ mv64xxx_i2c_fsm(struct mv64xxx_i2c_data *drv_data, u32 status)
break;
default:
- dev_err(&drv_data->adapter.dev,
+ dev_err(drv_data->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,
@@ -245,16 +267,10 @@ mv64xxx_i2c_fsm(struct mv64xxx_i2c_data *drv_data, u32 status)
static void
mv64xxx_i2c_do_action(struct mv64xxx_i2c_data *drv_data)
{
+ dev_dbg(drv_data->dev,
+ "do_action: state: 0x%x, action: 0x%x\n",
+ drv_data->state, drv_data->action);
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);
- drv_data->block = 0;
- wake_up_interruptible(&drv_data->waitq);
- break;
-
case MV64XXX_I2C_ACTION_CONTINUE:
writel(drv_data->cntl_bits,
drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
@@ -263,6 +279,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:
@@ -297,15 +318,26 @@ mv64xxx_i2c_do_action(struct mv64xxx_i2c_data *drv_data)
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->reg_base + MV64XXX_I2C_REG_CONTROL);
+ mv64xxx_i2c_wait_after_stop(drv_data);
drv_data->block = 0;
- wake_up_interruptible(&drv_data->waitq);
+ wake_up(&drv_data->waitq);
+ break;
+ case MV64XXX_I2C_ACTION_NO_STOP:
+ /* 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(&drv_data->waitq);
break;
case MV64XXX_I2C_ACTION_INVALID:
default:
- dev_err(&drv_data->adapter.dev,
+ dev_err(drv_data->dev,
"mv64xxx_i2c_do_action: Invalid action: %d\n",
drv_data->action);
drv_data->rc = -EIO;
@@ -314,8 +346,9 @@ 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);
+ wake_up(&drv_data->waitq);
break;
}
}
@@ -332,9 +365,14 @@ mv64xxx_i2c_intr(int irq, void *dev_id)
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);
+ dev_dbg(drv_data->dev,
+ "intr: status: 0x%x, \n", status);
+
mv64xxx_i2c_fsm(drv_data, status);
mv64xxx_i2c_do_action(drv_data);
rc = IRQ_HANDLED;
+ if (drv_data->state == MV64XXX_I2C_STATE_WAITING_FOR_REPEATED_START)
+ break;
}
spin_unlock_irqrestore(&drv_data->lock, flags);
@@ -365,6 +403,9 @@ mv64xxx_i2c_prepare_for_io(struct mv64xxx_i2c_data *drv_data,
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;
@@ -399,10 +440,11 @@ mv64xxx_i2c_wait_for_completion(struct mv64xxx_i2c_data *drv_data)
time_left = wait_event_timeout(drv_data->waitq,
!drv_data->block, drv_data->adapter.timeout);
+ drv_data->rc = time_left;
if ((time_left <= 0) && drv_data->block) {
drv_data->state = MV64XXX_I2C_STATE_IDLE;
- dev_err(&drv_data->adapter.dev,
+ dev_err(drv_data->dev,
"mv64xxx: I2C bus locked, block: %d, "
"time_left: %d\n", drv_data->block,
(int)time_left);
@@ -413,8 +455,7 @@ mv64xxx_i2c_wait_for_completion(struct mv64xxx_i2c_data *drv_data)
}
static int
-mv64xxx_i2c_execute_msg(struct mv64xxx_i2c_data *drv_data, struct i2c_msg *msg,
- int is_first, int is_last)
+mv64xxx_i2c_execute_msg(struct mv64xxx_i2c_data *drv_data, struct i2c_msg *msg)
{
unsigned long flags;
@@ -434,18 +475,10 @@ mv64xxx_i2c_execute_msg(struct mv64xxx_i2c_data *drv_data, struct i2c_msg *msg,
drv_data->bytes_left--;
}
} else {
- if (is_first) {
- drv_data->action = MV64XXX_I2C_ACTION_SEND_START;
- drv_data->state =
- MV64XXX_I2C_STATE_WAITING_FOR_START_COND;
- } else {
- drv_data->action = MV64XXX_I2C_ACTION_SEND_ADDR_1;
- drv_data->state =
- MV64XXX_I2C_STATE_WAITING_FOR_ADDR_1_ACK;
- }
+ drv_data->action = MV64XXX_I2C_ACTION_SEND_START;
+ drv_data->state = MV64XXX_I2C_STATE_WAITING_FOR_START_COND;
}
- drv_data->send_stop = is_last;
drv_data->block = 1;
mv64xxx_i2c_do_action(drv_data);
spin_unlock_irqrestore(&drv_data->lock, flags);
@@ -466,28 +499,38 @@ 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;
- for (i = 0; i < num; i++) {
- rc = mv64xxx_i2c_execute_msg(drv_data, &msgs[i],
- i == 0, i + 1 == num);
- if (rc < 0)
+ for (i=0; i<num; i++)
+ {
+ if(num > 1 && (i != num-1))
+ {
+ /*
+ * if combine access, we don't send stop signal
+ * between msgs.
+ */
+ drv_data->combine_access = 1;
+ dev_dbg(drv_data->dev,
+ "xfer: combine_access\n");
+
+ }else{
+ drv_data->combine_access = 0;
+ }
+
+ if ((rc = mv64xxx_i2c_execute_msg(drv_data, &msgs[i])) < 0)
return rc;
}
-
return num;
}
static const struct i2c_algorithm mv64xxx_i2c_algo = {
.master_xfer = mv64xxx_i2c_xfer,
.functionality = mv64xxx_i2c_functionality,
};
-
/*
*****************************************************************************
*
@@ -507,7 +550,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, pd->name))
return -EBUSY;
drv_data->reg_base = ioremap(r->start, size);
@@ -618,7 +661,6 @@ mv64xxx_i2c_probe(struct platform_device *pd)
drv_data = kzalloc(sizeof(struct mv64xxx_i2c_data), GFP_KERNEL);
if (!drv_data)
return -ENOMEM;
-
if (mv64xxx_i2c_map_regs(pd, drv_data)) {
rc = -ENODEV;
goto exit_kfree;
@@ -643,6 +685,9 @@ mv64xxx_i2c_probe(struct platform_device *pd)
drv_data->freq_n = pdata->freq_n;
drv_data->irq = platform_get_irq(pd, 0);
drv_data->adapter.timeout = msecs_to_jiffies(pdata->timeout);
+ drv_data->delay_after_stop = pdata->delay_after_stop ?
+ pdata->delay_after_stop : 1;
+
} else if (pd->dev.of_node) {
rc = mv64xxx_of_config(drv_data, pd->dev.of_node);
if (rc)
@@ -653,6 +698,8 @@ mv64xxx_i2c_probe(struct platform_device *pd)
goto exit_unmap_regs;
}
+ drv_data->irq_disabled = 0;
+ drv_data->dev = &pd->dev;
drv_data->adapter.dev.parent = &pd->dev;
drv_data->adapter.algo = &mv64xxx_i2c_algo;
drv_data->adapter.owner = THIS_MODULE;
@@ -701,9 +748,8 @@ 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);
free_irq(drv_data->irq, drv_data);
mv64xxx_i2c_unmap_regs(drv_data);
#if defined(CONFIG_HAVE_CLK)
@@ -734,7 +780,19 @@ static struct platform_driver mv64xxx_i2c_driver = {
},
};
-module_platform_driver(mv64xxx_i2c_driver);
+static int __init mv64xxx_i2c_init(void)
+{
+ int rc = platform_driver_register(&mv64xxx_i2c_driver);
+ return rc;
+}
+
+static void __exit mv64xxx_i2c_exit(void)
+{
+ platform_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");
View
1 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 */
};

0 comments on commit 6a661ac

Please sign in to comment.