Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 358789
b: refs/heads/master
c: fc91e40
h: refs/heads/master
i:
  358787: 34be7e1
v: v3
  • Loading branch information
Marek Vasut authored and Wolfram Sang committed Jan 28, 2013
1 parent f5873a1 commit b84d77c
Show file tree
Hide file tree
Showing 2 changed files with 164 additions and 14 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: dc6fea4456aeadd3452638668101f269fbb9fd0d
refs/heads/master: fc91e401239a451f7f4269de0b0ae6d70d856d48
176 changes: 163 additions & 13 deletions trunk/drivers/i2c/busses/i2c-mxs.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
#define MXS_I2C_CTRL0_SET (0x04)

#define MXS_I2C_CTRL0_SFTRST 0x80000000
#define MXS_I2C_CTRL0_RUN 0x20000000
#define MXS_I2C_CTRL0_SEND_NAK_ON_LAST 0x02000000
#define MXS_I2C_CTRL0_RETAIN_CLOCK 0x00200000
#define MXS_I2C_CTRL0_POST_SEND_STOP 0x00100000
Expand All @@ -64,6 +65,13 @@
#define MXS_I2C_CTRL1_SLAVE_STOP_IRQ 0x02
#define MXS_I2C_CTRL1_SLAVE_IRQ 0x01

#define MXS_I2C_DATA (0xa0)

#define MXS_I2C_DEBUG0 (0xb0)
#define MXS_I2C_DEBUG0_CLR (0xb8)

#define MXS_I2C_DEBUG0_DMAREQ 0x80000000

#define MXS_I2C_IRQ_MASK (MXS_I2C_CTRL1_DATA_ENGINE_CMPLT_IRQ | \
MXS_I2C_CTRL1_NO_SLAVE_ACK_IRQ | \
MXS_I2C_CTRL1_EARLY_TERM_IRQ | \
Expand Down Expand Up @@ -298,6 +306,135 @@ static int mxs_i2c_dma_setup_xfer(struct i2c_adapter *adap,
return -EINVAL;
}

static int mxs_i2c_pio_wait_dmareq(struct mxs_i2c_dev *i2c)
{
unsigned long timeout = jiffies + msecs_to_jiffies(1000);

while (!(readl(i2c->regs + MXS_I2C_DEBUG0) &
MXS_I2C_DEBUG0_DMAREQ)) {
if (time_after(jiffies, timeout))
return -ETIMEDOUT;
cond_resched();
}

writel(MXS_I2C_DEBUG0_DMAREQ, i2c->regs + MXS_I2C_DEBUG0_CLR);

return 0;
}

static int mxs_i2c_pio_wait_cplt(struct mxs_i2c_dev *i2c)
{
unsigned long timeout = jiffies + msecs_to_jiffies(1000);

/*
* We do not use interrupts in the PIO mode. Due to the
* maximum transfer length being 8 bytes in PIO mode, the
* overhead of interrupt would be too large and this would
* neglect the gain from using the PIO mode.
*/

while (!(readl(i2c->regs + MXS_I2C_CTRL1) &
MXS_I2C_CTRL1_DATA_ENGINE_CMPLT_IRQ)) {
if (time_after(jiffies, timeout))
return -ETIMEDOUT;
cond_resched();
}

writel(MXS_I2C_CTRL1_DATA_ENGINE_CMPLT_IRQ,
i2c->regs + MXS_I2C_CTRL1_CLR);

return 0;
}

static int mxs_i2c_pio_setup_xfer(struct i2c_adapter *adap,
struct i2c_msg *msg, uint32_t flags)
{
struct mxs_i2c_dev *i2c = i2c_get_adapdata(adap);
uint32_t addr_data = msg->addr << 1;
uint32_t data = 0;
int i, shifts_left, ret;

/* Mute IRQs coming from this block. */
writel(MXS_I2C_IRQ_MASK << 8, i2c->regs + MXS_I2C_CTRL1_CLR);

if (msg->flags & I2C_M_RD) {
addr_data |= I2C_SMBUS_READ;

/* SELECT command. */
writel(MXS_I2C_CTRL0_RUN | MXS_CMD_I2C_SELECT,
i2c->regs + MXS_I2C_CTRL0);

ret = mxs_i2c_pio_wait_dmareq(i2c);
if (ret)
return ret;

writel(addr_data, i2c->regs + MXS_I2C_DATA);

ret = mxs_i2c_pio_wait_cplt(i2c);
if (ret)
return ret;

/* READ command. */
writel(MXS_I2C_CTRL0_RUN | MXS_CMD_I2C_READ | flags |
MXS_I2C_CTRL0_XFER_COUNT(msg->len),
i2c->regs + MXS_I2C_CTRL0);

for (i = 0; i < msg->len; i++) {
if ((i & 3) == 0) {
ret = mxs_i2c_pio_wait_dmareq(i2c);
if (ret)
return ret;
data = readl(i2c->regs + MXS_I2C_DATA);
}
msg->buf[i] = data & 0xff;
data >>= 8;
}
} else {
addr_data |= I2C_SMBUS_WRITE;

/* WRITE command. */
writel(MXS_I2C_CTRL0_RUN | MXS_CMD_I2C_WRITE | flags |
MXS_I2C_CTRL0_XFER_COUNT(msg->len + 1),
i2c->regs + MXS_I2C_CTRL0);

/*
* The LSB of data buffer is the first byte blasted across
* the bus. Higher order bytes follow. Thus the following
* filling schematic.
*/
data = addr_data << 24;
for (i = 0; i < msg->len; i++) {
data >>= 8;
data |= (msg->buf[i] << 24);
if ((i & 3) == 2) {
ret = mxs_i2c_pio_wait_dmareq(i2c);
if (ret)
return ret;
writel(data, i2c->regs + MXS_I2C_DATA);
}
}

shifts_left = 24 - (i & 3) * 8;
if (shifts_left) {
data >>= shifts_left;
ret = mxs_i2c_pio_wait_dmareq(i2c);
if (ret)
return ret;
writel(data, i2c->regs + MXS_I2C_DATA);
}
}

ret = mxs_i2c_pio_wait_cplt(i2c);
if (ret)
return ret;

/* Clear any dangling IRQs and re-enable interrupts. */
writel(MXS_I2C_IRQ_MASK, i2c->regs + MXS_I2C_CTRL1_CLR);
writel(MXS_I2C_IRQ_MASK << 8, i2c->regs + MXS_I2C_CTRL1_SET);

return 0;
}

/*
* Low level master read/write transaction.
*/
Expand All @@ -316,24 +453,37 @@ static int mxs_i2c_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg,
if (msg->len == 0)
return -EINVAL;

INIT_COMPLETION(i2c->cmd_complete);
i2c->cmd_err = 0;

ret = mxs_i2c_dma_setup_xfer(adap, msg, flags);
if (ret)
return ret;
/*
* The current boundary to select between PIO/DMA transfer method
* is set to 8 bytes, transfers shorter than 8 bytes are transfered
* using PIO mode while longer transfers use DMA. The 8 byte border is
* based on this empirical measurement and a lot of previous frobbing.
*/
if (msg->len < 8) {
ret = mxs_i2c_pio_setup_xfer(adap, msg, flags);
if (ret)
mxs_i2c_reset(i2c);
} else {
i2c->cmd_err = 0;
INIT_COMPLETION(i2c->cmd_complete);
ret = mxs_i2c_dma_setup_xfer(adap, msg, flags);
if (ret)
return ret;

ret = wait_for_completion_timeout(&i2c->cmd_complete,
ret = wait_for_completion_timeout(&i2c->cmd_complete,
msecs_to_jiffies(1000));
if (ret == 0)
goto timeout;
if (ret == 0)
goto timeout;

if (i2c->cmd_err == -ENXIO)
mxs_i2c_reset(i2c);

if (i2c->cmd_err == -ENXIO)
mxs_i2c_reset(i2c);
ret = i2c->cmd_err;
}

dev_dbg(i2c->dev, "Done with err=%d\n", i2c->cmd_err);
dev_dbg(i2c->dev, "Done with err=%d\n", ret);

return i2c->cmd_err;
return ret;

timeout:
dev_dbg(i2c->dev, "Timeout!\n");
Expand Down

0 comments on commit b84d77c

Please sign in to comment.