Revert "i2c: tegra: Remove the synchronization between isr and caller"
Alok Chauhan [Tue, 16 Aug 2011 00:47:40 +0000 (17:47 -0700)]
This reverts commit 738c1a66079ee7db8e4365aa85819ccdef2592a8.
This fixed the video recording system hang bug

Bug 842901

Reviewed-on: http://git-master/r/39912

Rebase-Id: R183a5a9202ec65099503ddad913a25b139bd6d91

drivers/i2c/busses/i2c-tegra.c

index b9f155c..58cf703 100644 (file)
@@ -146,10 +146,12 @@ struct tegra_i2c_dev {
        struct clk *clk;
        struct resource *iomem;
        struct rt_mutex dev_lock;
+       spinlock_t clk_lock;
        void __iomem *base;
        int cont_id;
        int irq;
        bool irq_disabled;
+       bool controller_enabled;
        int is_dvc;
        bool is_slave;
        struct completion msg_complete;
@@ -444,6 +446,14 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id)
        u32 status;
        const u32 status_err = I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST | I2C_INT_TX_FIFO_OVERFLOW;
        struct tegra_i2c_dev *i2c_dev = dev_id;
+       unsigned long flags;
+
+       spin_lock_irqsave(&i2c_dev->clk_lock, flags);
+       if (!i2c_dev->controller_enabled) {
+               dev_warn(i2c_dev->dev, "Controller not enabled\n");
+               spin_unlock_irqrestore(&i2c_dev->clk_lock, flags);
+               return IRQ_NONE;
+       }
 
        status = i2c_readl(i2c_dev, I2C_INT_STATUS);
 
@@ -457,7 +467,6 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id)
                        i2c_dev->irq_disabled = 1;
                }
 
-               complete(&i2c_dev->msg_complete);
                goto err;
        }
 
@@ -486,8 +495,6 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id)
                        dev_warn(i2c_dev->dev, "Packet status 0x%08x\n",
                                i2c_readl(i2c_dev, I2C_PACKET_TRANSFER_STATUS));
                }
-               complete(&i2c_dev->msg_complete);
-
                goto err;
        }
 
@@ -503,7 +510,6 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id)
                        i2c_dev->irq_disabled = 1;
                }
 
-               complete(&i2c_dev->msg_complete);
                goto err;
        }
 
@@ -521,15 +527,17 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id)
                        tegra_i2c_mask_irq(i2c_dev, I2C_INT_TX_FIFO_DATA_REQ);
        }
 
-       if (status & I2C_INT_PACKET_XFER_COMPLETE) {
-               BUG_ON(i2c_dev->msg_buf_remaining);
-               complete(&i2c_dev->msg_complete);
-       }
        i2c_writel(i2c_dev, status, I2C_INT_STATUS);
 
        if (i2c_dev->is_dvc)
                dvc_writel(i2c_dev, DVC_STATUS_I2C_DONE_INTR, DVC_STATUS);
 
+       if (status & I2C_INT_PACKET_XFER_COMPLETE) {
+               BUG_ON(i2c_dev->msg_buf_remaining);
+               complete(&i2c_dev->msg_complete);
+       }
+
+       spin_unlock_irqrestore(&i2c_dev->clk_lock, flags);
        return IRQ_HANDLED;
 
 err:
@@ -563,6 +571,8 @@ err:
        if (i2c_dev->is_dvc)
                dvc_writel(i2c_dev, DVC_STATUS_I2C_DONE_INTR, DVC_STATUS);
 
+       complete(&i2c_dev->msg_complete);
+       spin_unlock_irqrestore(&i2c_dev->clk_lock, flags);
        return IRQ_HANDLED;
 }
 
@@ -572,6 +582,7 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_bus *i2c_bus,
        struct tegra_i2c_dev *i2c_dev = i2c_bus->dev;
        u32 int_mask;
        int ret;
+       unsigned long flags;
 
        tegra_i2c_flush_fifos(i2c_dev);
 
@@ -628,7 +639,10 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_bus *i2c_bus,
                        "i2c transfer timed out, addr 0x%04x, data 0x%02x\n",
                        msg->addr, msg->buf[0]);
 
+               spin_lock_irqsave(&i2c_dev->clk_lock, flags);
+               i2c_dev->controller_enabled = false;
                tegra_i2c_init(i2c_dev);
+               spin_unlock_irqrestore(&i2c_dev->clk_lock, flags);
                return -ETIMEDOUT;
        }
 
@@ -638,7 +652,11 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_bus *i2c_bus,
        if (likely(i2c_dev->msg_err == I2C_ERR_NONE))
                return 0;
 
+       spin_lock_irqsave(&i2c_dev->clk_lock, flags);
+       i2c_dev->controller_enabled = false;
        tegra_i2c_init(i2c_dev);
+       spin_unlock_irqrestore(&i2c_dev->clk_lock, flags);
+
        if (i2c_dev->msg_err == I2C_ERR_NO_ACK) {
                if (msg->flags & I2C_M_IGNORE_NAK)
                        return 0;
@@ -658,6 +676,7 @@ static int tegra_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
        struct tegra_i2c_dev *i2c_dev = i2c_bus->dev;
        int i;
        int ret = 0;
+       unsigned long flags;
 
        if (i2c_dev->is_suspended)
                return -EBUSY;
@@ -683,6 +702,11 @@ static int tegra_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
 
        if (!i2c_dev->is_clkon_always)
                clk_enable(i2c_dev->clk);
+
+       spin_lock_irqsave(&i2c_dev->clk_lock, flags);
+       i2c_dev->controller_enabled = true;
+       spin_unlock_irqrestore(&i2c_dev->clk_lock, flags);
+
        for (i = 0; i < num; i++) {
                int stop = (i == (num - 1)) ? 1  : 0;
                ret = tegra_i2c_xfer_msg(i2c_bus, &msgs[i], stop);
@@ -690,6 +714,10 @@ static int tegra_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
                        break;
        }
 
+       spin_lock_irqsave(&i2c_dev->clk_lock, flags);
+       i2c_dev->controller_enabled = false;
+       spin_unlock_irqrestore(&i2c_dev->clk_lock, flags);
+
        if (!i2c_dev->is_clkon_always)
                clk_disable(i2c_dev->clk);
 
@@ -799,7 +827,9 @@ static int tegra_i2c_probe(struct platform_device *pdev)
 
        i2c_dev->msgs = NULL;
        i2c_dev->msgs_num = 0;
+       i2c_dev->controller_enabled = false;
        rt_mutex_init(&i2c_dev->dev_lock);
+       spin_lock_init(&i2c_dev->clk_lock);
 
        i2c_dev->slave_addr = plat->slave_addr;
        i2c_dev->is_dvc = plat->is_dvc;