]> git.karo-electronics.de Git - linux-beck.git/blobdiff - drivers/i2c/busses/i2c-tegra.c
Merge branch 'drm-nouveau-next' of git://anongit.freedesktop.org/git/nouveau/linux...
[linux-beck.git] / drivers / i2c / busses / i2c-tegra.c
index b4ab39b741eb768bd1a0d733b9329546fd0fe428..fb3b4f8f8152f41879ab736ee2c758332f1facb8 100644 (file)
 #define BYTES_PER_FIFO_WORD 4
 
 #define I2C_CNFG                               0x000
+#define I2C_CNFG_DEBOUNCE_CNT_SHIFT            12
 #define I2C_CNFG_PACKET_MODE_EN                        (1<<10)
 #define I2C_CNFG_NEW_MASTER_FSM                        (1<<11)
+#define I2C_STATUS                             0x01C
 #define I2C_SL_CNFG                            0x020
+#define I2C_SL_CNFG_NACK                       (1<<1)
 #define I2C_SL_CNFG_NEWSL                      (1<<2)
 #define I2C_SL_ADDR1                           0x02c
+#define I2C_SL_ADDR2                           0x030
 #define I2C_TX_FIFO                            0x050
 #define I2C_RX_FIFO                            0x054
 #define I2C_PACKET_TRANSFER_STATUS             0x058
@@ -77,6 +81,7 @@
 #define I2C_ERR_NONE                           0x00
 #define I2C_ERR_NO_ACK                         0x01
 #define I2C_ERR_ARBITRATION_LOST               0x02
+#define I2C_ERR_UNKNOWN_INTERRUPT              0x04
 
 #define PACKET_HEADER0_HEADER_SIZE_SHIFT       28
 #define PACKET_HEADER0_PACKET_ID_SHIFT         16
@@ -121,6 +126,7 @@ struct tegra_i2c_dev {
        void __iomem *base;
        int cont_id;
        int irq;
+       bool irq_disabled;
        int is_dvc;
        struct completion msg_complete;
        int msg_err;
@@ -325,11 +331,21 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
        if (i2c_dev->is_dvc)
                tegra_dvc_init(i2c_dev);
 
-       val = I2C_CNFG_NEW_MASTER_FSM | I2C_CNFG_PACKET_MODE_EN;
+       val = I2C_CNFG_NEW_MASTER_FSM | I2C_CNFG_PACKET_MODE_EN |
+               (0x2 << I2C_CNFG_DEBOUNCE_CNT_SHIFT);
        i2c_writel(i2c_dev, val, I2C_CNFG);
        i2c_writel(i2c_dev, 0, I2C_INT_MASK);
        clk_set_rate(i2c_dev->clk, i2c_dev->bus_clk_rate * 8);
 
+       if (!i2c_dev->is_dvc) {
+               u32 sl_cfg = i2c_readl(i2c_dev, I2C_SL_CNFG);
+               sl_cfg |= I2C_SL_CNFG_NACK | I2C_SL_CNFG_NEWSL;
+               i2c_writel(i2c_dev, sl_cfg, I2C_SL_CNFG);
+               i2c_writel(i2c_dev, 0xfc, I2C_SL_ADDR1);
+               i2c_writel(i2c_dev, 0x00, I2C_SL_ADDR2);
+
+       }
+
        val = 7 << I2C_FIFO_CONTROL_TX_TRIG_SHIFT |
                0 << I2C_FIFO_CONTROL_RX_TRIG_SHIFT;
        i2c_writel(i2c_dev, val, I2C_FIFO_CONTROL);
@@ -338,6 +354,12 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
                err = -ETIMEDOUT;
 
        clk_disable(i2c_dev->clk);
+
+       if (i2c_dev->irq_disabled) {
+               i2c_dev->irq_disabled = 0;
+               enable_irq(i2c_dev->irq);
+       }
+
        return err;
 }
 
@@ -350,8 +372,19 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id)
        status = i2c_readl(i2c_dev, I2C_INT_STATUS);
 
        if (status == 0) {
-               dev_warn(i2c_dev->dev, "interrupt with no status\n");
-               return IRQ_NONE;
+               dev_warn(i2c_dev->dev, "irq status 0 %08x %08x %08x\n",
+                        i2c_readl(i2c_dev, I2C_PACKET_TRANSFER_STATUS),
+                        i2c_readl(i2c_dev, I2C_STATUS),
+                        i2c_readl(i2c_dev, I2C_CNFG));
+               i2c_dev->msg_err |= I2C_ERR_UNKNOWN_INTERRUPT;
+
+               if (!i2c_dev->irq_disabled) {
+                       disable_irq_nosync(i2c_dev->irq);
+                       i2c_dev->irq_disabled = 1;
+               }
+
+               complete(&i2c_dev->msg_complete);
+               goto err;
        }
 
        if (unlikely(status & status_err)) {
@@ -391,6 +424,8 @@ err:
                I2C_INT_PACKET_XFER_COMPLETE | I2C_INT_TX_FIFO_DATA_REQ |
                I2C_INT_RX_FIFO_DATA_REQ);
        i2c_writel(i2c_dev, status, I2C_INT_STATUS);
+       if (i2c_dev->is_dvc)
+               dvc_writel(i2c_dev, DVC_STATUS_I2C_DONE_INTR, DVC_STATUS);
        return IRQ_HANDLED;
 }
 
@@ -424,12 +459,12 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
 
        packet_header = msg->addr << I2C_HEADER_SLAVE_ADDR_SHIFT;
        packet_header |= I2C_HEADER_IE_ENABLE;
+       if (!stop)
+               packet_header |= I2C_HEADER_REPEAT_START;
        if (msg->flags & I2C_M_TEN)
                packet_header |= I2C_HEADER_10BIT_ADDR;
        if (msg->flags & I2C_M_IGNORE_NAK)
                packet_header |= I2C_HEADER_CONT_ON_NAK;
-       if (msg->flags & I2C_M_NOSTART)
-               packet_header |= I2C_HEADER_REPEAT_START;
        if (msg->flags & I2C_M_RD)
                packet_header |= I2C_HEADER_READ;
        i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO);