Skip to content

Commit

Permalink
i2c: sh_mobile: don't send a stop condition by default inside transfers
Browse files Browse the repository at this point in the history
By default there should be no stop bit on I2C between single messages
within transfers. Fix the driver to comply and only send a stop bit at
the end of transfers or if I2C_M_STOP is set.

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
Signed-off-by: Wolfram Sang <wolfram@the-dreams.de>
  • Loading branch information
Guennadi Liakhovetski authored and Wolfram Sang committed Feb 10, 2013
1 parent 4b38231 commit e789029
Showing 1 changed file with 59 additions and 20 deletions.
79 changes: 59 additions & 20 deletions drivers/i2c/busses/i2c-sh_mobile.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,21 +38,21 @@
/* Transmit operation: */
/* */
/* 0 byte transmit */
/* BUS: S A8 ACK P */
/* BUS: S A8 ACK P(*) */
/* IRQ: DTE WAIT */
/* ICIC: */
/* ICCR: 0x94 0x90 */
/* ICDR: A8 */
/* */
/* 1 byte transmit */
/* BUS: S A8 ACK D8(1) ACK P */
/* BUS: S A8 ACK D8(1) ACK P(*) */
/* IRQ: DTE WAIT WAIT */
/* ICIC: -DTE */
/* ICCR: 0x94 0x90 */
/* ICDR: A8 D8(1) */
/* */
/* 2 byte transmit */
/* BUS: S A8 ACK D8(1) ACK D8(2) ACK P */
/* BUS: S A8 ACK D8(1) ACK D8(2) ACK P(*) */
/* IRQ: DTE WAIT WAIT WAIT */
/* ICIC: -DTE */
/* ICCR: 0x94 0x90 */
Expand All @@ -66,20 +66,20 @@
/* 0 byte receive - not supported since slave may hold SDA low */
/* */
/* 1 byte receive [TX] | [RX] */
/* BUS: S A8 ACK | D8(1) ACK P */
/* BUS: S A8 ACK | D8(1) ACK P(*) */
/* IRQ: DTE WAIT | WAIT DTE */
/* ICIC: -DTE | +DTE */
/* ICCR: 0x94 0x81 | 0xc0 */
/* ICDR: A8 | D8(1) */
/* */
/* 2 byte receive [TX]| [RX] */
/* BUS: S A8 ACK | D8(1) ACK D8(2) ACK P */
/* BUS: S A8 ACK | D8(1) ACK D8(2) ACK P(*) */
/* IRQ: DTE WAIT | WAIT WAIT DTE */
/* ICIC: -DTE | +DTE */
/* ICCR: 0x94 0x81 | 0xc0 */
/* ICDR: A8 | D8(1) D8(2) */
/* */
/* 3 byte receive [TX] | [RX] */
/* 3 byte receive [TX] | [RX] (*) */
/* BUS: S A8 ACK | D8(1) ACK D8(2) ACK D8(3) ACK P */
/* IRQ: DTE WAIT | WAIT WAIT WAIT DTE */
/* ICIC: -DTE | +DTE */
Expand All @@ -94,7 +94,7 @@
/* SDA ___\___XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXAAAAAAAAA___/ */
/* SCL \_/1\_/2\_/3\_/4\_/5\_/6\_/7\_/8\___/9\_____/ */
/* */
/* S D7 D6 D5 D4 D3 D2 D1 D0 P */
/* S D7 D6 D5 D4 D3 D2 D1 D0 P(*) */
/* ___ */
/* WAIT IRQ ________________________________/ \___________ */
/* TACK IRQ ____________________________________/ \_______ */
Expand All @@ -103,6 +103,11 @@
/* _______________________________________________ */
/* BUSY __/ \_ */
/* */
/* (*) The STOP condition is only sent by the master at the end of the last */
/* I2C message or if the I2C_M_STOP flag is set. Similarly, the BUSY bit is */
/* only cleared after the STOP condition, so, between messages we have to */
/* poll for the DTE bit. */
/* */

enum sh_mobile_i2c_op {
OP_START = 0,
Expand Down Expand Up @@ -132,6 +137,7 @@ struct sh_mobile_i2c_data {
struct i2c_msg *msg;
int pos;
int sr;
bool send_stop;
};

#define IIC_FLAG_HAS_ICIC67 (1 << 0)
Expand Down Expand Up @@ -322,7 +328,7 @@ static unsigned char i2c_op(struct sh_mobile_i2c_data *pd,
break;
case OP_TX_STOP: /* write data and issue a stop afterwards */
iic_wr(pd, ICDR, data);
iic_wr(pd, ICCR, 0x90);
iic_wr(pd, ICCR, pd->send_stop ? 0x90 : 0x94);
break;
case OP_TX_TO_RX: /* select read mode */
iic_wr(pd, ICCR, 0x81);
Expand Down Expand Up @@ -469,22 +475,25 @@ static irqreturn_t sh_mobile_i2c_isr(int irq, void *dev_id)
return IRQ_HANDLED;
}

static int start_ch(struct sh_mobile_i2c_data *pd, struct i2c_msg *usr_msg)
static int start_ch(struct sh_mobile_i2c_data *pd, struct i2c_msg *usr_msg,
bool do_init)
{
if (usr_msg->len == 0 && (usr_msg->flags & I2C_M_RD)) {
dev_err(pd->dev, "Unsupported zero length i2c read\n");
return -EIO;
}

/* Initialize channel registers */
iic_set_clr(pd, ICCR, 0, ICCR_ICE);
if (do_init) {
/* Initialize channel registers */
iic_set_clr(pd, ICCR, 0, ICCR_ICE);

/* Enable channel and configure rx ack */
iic_set_clr(pd, ICCR, ICCR_ICE, 0);
/* Enable channel and configure rx ack */
iic_set_clr(pd, ICCR, ICCR_ICE, 0);

/* Set the clock */
iic_wr(pd, ICCL, pd->iccl & 0xff);
iic_wr(pd, ICCH, pd->icch & 0xff);
/* Set the clock */
iic_wr(pd, ICCL, pd->iccl & 0xff);
iic_wr(pd, ICCH, pd->icch & 0xff);
}

pd->msg = usr_msg;
pd->pos = -1;
Expand All @@ -495,6 +504,30 @@ static int start_ch(struct sh_mobile_i2c_data *pd, struct i2c_msg *usr_msg)
return 0;
}

static int poll_dte(struct sh_mobile_i2c_data *pd)
{
int i;

for (i = 1000; i; i--) {
u_int8_t val = iic_rd(pd, ICSR);

if (val & ICSR_DTE)
break;

if (val & ICSR_TACK)
return -EIO;

udelay(10);
}

if (!i) {
dev_warn(pd->dev, "Timeout polling for DTE!\n");
return -ETIMEDOUT;
}

return 0;
}

static int poll_busy(struct sh_mobile_i2c_data *pd)
{
int i;
Expand Down Expand Up @@ -539,13 +572,16 @@ static int sh_mobile_i2c_xfer(struct i2c_adapter *adapter,

/* Process all messages */
for (i = 0; i < num; i++) {
bool do_start = pd->send_stop || !i;
msg = &msgs[i];
pd->send_stop = i == num - 1 || msg->flags & I2C_M_STOP;

err = start_ch(pd, msg);
err = start_ch(pd, msg, do_start);
if (err)
break;

i2c_op(pd, OP_START, 0);
if (do_start)
i2c_op(pd, OP_START, 0);

/* The interrupt handler takes care of the rest... */
k = wait_event_timeout(pd->wait,
Expand All @@ -557,7 +593,10 @@ static int sh_mobile_i2c_xfer(struct i2c_adapter *adapter,
break;
}

err = poll_busy(pd);
if (pd->send_stop)
err = poll_busy(pd);
else
err = poll_dte(pd);
if (err < 0)
break;
}
Expand All @@ -571,7 +610,7 @@ static int sh_mobile_i2c_xfer(struct i2c_adapter *adapter,

static u32 sh_mobile_i2c_func(struct i2c_adapter *adapter)
{
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_PROTOCOL_MANGLING;
}

static struct i2c_algorithm sh_mobile_i2c_algorithm = {
Expand Down

0 comments on commit e789029

Please sign in to comment.