diff options
Diffstat (limited to 'drivers/i2c/busses/i2c-tegra.c')
-rw-r--r-- | drivers/i2c/busses/i2c-tegra.c | 39 |
1 files changed, 34 insertions, 5 deletions
diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c index b4ab39b741eb..4d9319665e32 100644 --- a/drivers/i2c/busses/i2c-tegra.c +++ b/drivers/i2c/busses/i2c-tegra.c | |||
@@ -35,8 +35,10 @@ | |||
35 | #define BYTES_PER_FIFO_WORD 4 | 35 | #define BYTES_PER_FIFO_WORD 4 |
36 | 36 | ||
37 | #define I2C_CNFG 0x000 | 37 | #define I2C_CNFG 0x000 |
38 | #define I2C_CNFG_DEBOUNCE_CNT_SHIFT 12 | ||
38 | #define I2C_CNFG_PACKET_MODE_EN (1<<10) | 39 | #define I2C_CNFG_PACKET_MODE_EN (1<<10) |
39 | #define I2C_CNFG_NEW_MASTER_FSM (1<<11) | 40 | #define I2C_CNFG_NEW_MASTER_FSM (1<<11) |
41 | #define I2C_STATUS 0x01C | ||
40 | #define I2C_SL_CNFG 0x020 | 42 | #define I2C_SL_CNFG 0x020 |
41 | #define I2C_SL_CNFG_NEWSL (1<<2) | 43 | #define I2C_SL_CNFG_NEWSL (1<<2) |
42 | #define I2C_SL_ADDR1 0x02c | 44 | #define I2C_SL_ADDR1 0x02c |
@@ -77,6 +79,7 @@ | |||
77 | #define I2C_ERR_NONE 0x00 | 79 | #define I2C_ERR_NONE 0x00 |
78 | #define I2C_ERR_NO_ACK 0x01 | 80 | #define I2C_ERR_NO_ACK 0x01 |
79 | #define I2C_ERR_ARBITRATION_LOST 0x02 | 81 | #define I2C_ERR_ARBITRATION_LOST 0x02 |
82 | #define I2C_ERR_UNKNOWN_INTERRUPT 0x04 | ||
80 | 83 | ||
81 | #define PACKET_HEADER0_HEADER_SIZE_SHIFT 28 | 84 | #define PACKET_HEADER0_HEADER_SIZE_SHIFT 28 |
82 | #define PACKET_HEADER0_PACKET_ID_SHIFT 16 | 85 | #define PACKET_HEADER0_PACKET_ID_SHIFT 16 |
@@ -121,6 +124,7 @@ struct tegra_i2c_dev { | |||
121 | void __iomem *base; | 124 | void __iomem *base; |
122 | int cont_id; | 125 | int cont_id; |
123 | int irq; | 126 | int irq; |
127 | bool irq_disabled; | ||
124 | int is_dvc; | 128 | int is_dvc; |
125 | struct completion msg_complete; | 129 | struct completion msg_complete; |
126 | int msg_err; | 130 | int msg_err; |
@@ -325,11 +329,17 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev) | |||
325 | if (i2c_dev->is_dvc) | 329 | if (i2c_dev->is_dvc) |
326 | tegra_dvc_init(i2c_dev); | 330 | tegra_dvc_init(i2c_dev); |
327 | 331 | ||
328 | val = I2C_CNFG_NEW_MASTER_FSM | I2C_CNFG_PACKET_MODE_EN; | 332 | val = I2C_CNFG_NEW_MASTER_FSM | I2C_CNFG_PACKET_MODE_EN | |
333 | (0x2 << I2C_CNFG_DEBOUNCE_CNT_SHIFT); | ||
329 | i2c_writel(i2c_dev, val, I2C_CNFG); | 334 | i2c_writel(i2c_dev, val, I2C_CNFG); |
330 | i2c_writel(i2c_dev, 0, I2C_INT_MASK); | 335 | i2c_writel(i2c_dev, 0, I2C_INT_MASK); |
331 | clk_set_rate(i2c_dev->clk, i2c_dev->bus_clk_rate * 8); | 336 | clk_set_rate(i2c_dev->clk, i2c_dev->bus_clk_rate * 8); |
332 | 337 | ||
338 | if (!i2c_dev->is_dvc) { | ||
339 | u32 sl_cfg = i2c_readl(i2c_dev, I2C_SL_CNFG); | ||
340 | i2c_writel(i2c_dev, sl_cfg | I2C_SL_CNFG_NEWSL, I2C_SL_CNFG); | ||
341 | } | ||
342 | |||
333 | val = 7 << I2C_FIFO_CONTROL_TX_TRIG_SHIFT | | 343 | val = 7 << I2C_FIFO_CONTROL_TX_TRIG_SHIFT | |
334 | 0 << I2C_FIFO_CONTROL_RX_TRIG_SHIFT; | 344 | 0 << I2C_FIFO_CONTROL_RX_TRIG_SHIFT; |
335 | i2c_writel(i2c_dev, val, I2C_FIFO_CONTROL); | 345 | i2c_writel(i2c_dev, val, I2C_FIFO_CONTROL); |
@@ -338,6 +348,12 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev) | |||
338 | err = -ETIMEDOUT; | 348 | err = -ETIMEDOUT; |
339 | 349 | ||
340 | clk_disable(i2c_dev->clk); | 350 | clk_disable(i2c_dev->clk); |
351 | |||
352 | if (i2c_dev->irq_disabled) { | ||
353 | i2c_dev->irq_disabled = 0; | ||
354 | enable_irq(i2c_dev->irq); | ||
355 | } | ||
356 | |||
341 | return err; | 357 | return err; |
342 | } | 358 | } |
343 | 359 | ||
@@ -350,8 +366,19 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id) | |||
350 | status = i2c_readl(i2c_dev, I2C_INT_STATUS); | 366 | status = i2c_readl(i2c_dev, I2C_INT_STATUS); |
351 | 367 | ||
352 | if (status == 0) { | 368 | if (status == 0) { |
353 | dev_warn(i2c_dev->dev, "interrupt with no status\n"); | 369 | dev_warn(i2c_dev->dev, "irq status 0 %08x %08x %08x\n", |
354 | return IRQ_NONE; | 370 | i2c_readl(i2c_dev, I2C_PACKET_TRANSFER_STATUS), |
371 | i2c_readl(i2c_dev, I2C_STATUS), | ||
372 | i2c_readl(i2c_dev, I2C_CNFG)); | ||
373 | i2c_dev->msg_err |= I2C_ERR_UNKNOWN_INTERRUPT; | ||
374 | |||
375 | if (!i2c_dev->irq_disabled) { | ||
376 | disable_irq_nosync(i2c_dev->irq); | ||
377 | i2c_dev->irq_disabled = 1; | ||
378 | } | ||
379 | |||
380 | complete(&i2c_dev->msg_complete); | ||
381 | goto err; | ||
355 | } | 382 | } |
356 | 383 | ||
357 | if (unlikely(status & status_err)) { | 384 | if (unlikely(status & status_err)) { |
@@ -391,6 +418,8 @@ err: | |||
391 | I2C_INT_PACKET_XFER_COMPLETE | I2C_INT_TX_FIFO_DATA_REQ | | 418 | I2C_INT_PACKET_XFER_COMPLETE | I2C_INT_TX_FIFO_DATA_REQ | |
392 | I2C_INT_RX_FIFO_DATA_REQ); | 419 | I2C_INT_RX_FIFO_DATA_REQ); |
393 | i2c_writel(i2c_dev, status, I2C_INT_STATUS); | 420 | i2c_writel(i2c_dev, status, I2C_INT_STATUS); |
421 | if (i2c_dev->is_dvc) | ||
422 | dvc_writel(i2c_dev, DVC_STATUS_I2C_DONE_INTR, DVC_STATUS); | ||
394 | return IRQ_HANDLED; | 423 | return IRQ_HANDLED; |
395 | } | 424 | } |
396 | 425 | ||
@@ -424,12 +453,12 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev, | |||
424 | 453 | ||
425 | packet_header = msg->addr << I2C_HEADER_SLAVE_ADDR_SHIFT; | 454 | packet_header = msg->addr << I2C_HEADER_SLAVE_ADDR_SHIFT; |
426 | packet_header |= I2C_HEADER_IE_ENABLE; | 455 | packet_header |= I2C_HEADER_IE_ENABLE; |
456 | if (!stop) | ||
457 | packet_header |= I2C_HEADER_REPEAT_START; | ||
427 | if (msg->flags & I2C_M_TEN) | 458 | if (msg->flags & I2C_M_TEN) |
428 | packet_header |= I2C_HEADER_10BIT_ADDR; | 459 | packet_header |= I2C_HEADER_10BIT_ADDR; |
429 | if (msg->flags & I2C_M_IGNORE_NAK) | 460 | if (msg->flags & I2C_M_IGNORE_NAK) |
430 | packet_header |= I2C_HEADER_CONT_ON_NAK; | 461 | packet_header |= I2C_HEADER_CONT_ON_NAK; |
431 | if (msg->flags & I2C_M_NOSTART) | ||
432 | packet_header |= I2C_HEADER_REPEAT_START; | ||
433 | if (msg->flags & I2C_M_RD) | 462 | if (msg->flags & I2C_M_RD) |
434 | packet_header |= I2C_HEADER_READ; | 463 | packet_header |= I2C_HEADER_READ; |
435 | i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO); | 464 | i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO); |