/* 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 */
/* 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 */
/* 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 ____________________________________/ \_______ */
/* _______________________________________________ */
/* 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,
struct i2c_msg *msg;
int pos;
int sr;
+ bool send_stop;
};
#define IIC_FLAG_HAS_ICIC67 (1 << 0)
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);
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;
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;
/* 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,
break;
}
- err = poll_busy(pd);
+ if (pd->send_stop)
+ err = poll_busy(pd);
+ else
+ err = poll_dte(pd);
if (err < 0)
break;
}
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 = {