i2c: fix i2c-sh_mobile timing issues
Subsystems (3)
PATCH 1/1 i2c: fix i2c-sh_mobile timing issues
From: Magnus Damm <[email protected]>
This patch teaches the i2c-sh_mobile driver to make use of wait irqs. Without this patch only dte irqs are used which may lead to overruns and cases of missing stop and extra bytes being read on the i2c bus.
Use of wait irqs forces the hardware to pause and wait until the cpu is ready. Polling is also reworked in this patch to fix ms delay issues.
Verified with bus analyzer and tested on MigoR and AP325RXA boards.
---
Paul, can you merge this for 2.6.27?
drivers/i2c/busses/i2c-sh_mobile.c | 271 +++++++++++++++++++++++++++--------- 1 file changed, 208 insertions(+), 63 deletions(-)
drivers/i2c/busses/i2c-sh_mobile.c +208 -63
@@ -31,13 +31,84 @@#include <linux/clk.h> #include <linux/io.h> +/* Transmit operation: */ +/* */ +/* 0 byte transmit */ +/* 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 */ +/* 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 */ +/* IRQ: DTE WAIT WAIT WAIT */ +/* ICIC: -DTE */ +/* ICCR: 0x94 0x90 */ +/* ICDR: A8 D8(1) D8(2) */ +/* */ +/* 3 bytes or more, +---------+ gets repeated */ +/* */ +/* */ +/* Receive operation: */ +/* */ +/* 0 byte receive - not supported since slave may hold SDA low */ +/* */ +/* 1 byte receive [TX] | [RX] */ +/* 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 */ +/* IRQ: DTE WAIT | WAIT WAIT DTE */ +/* ICIC: -DTE | +DTE */ +/* ICCR: 0x94 0x81 | 0xc0 */ +/* ICDR: A8 | D8(1) D8(2) */ +/* */ +/* 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 */ +/* ICCR: 0x94 0x81 | 0xc0 */ +/* ICDR: A8 | D8(1) D8(2) D8(3) */ +/* */ +/* 4 bytes or more, this part is repeated +---------+ */ +/* */ +/* */ +/* Interrupt order and BUSY flag */ +/* ___ _ */ +/* SDA ___\___XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXAAAAAAAAA___/ */ +/* SCL \_/1\_/2\_/3\_/4\_/5\_/6\_/7\_/8\___/9\_____/ */ +/* */ +/* S D7 D6 D5 D4 D3 D2 D1 D0 P */ +/* ___ */ +/* WAIT IRQ ________________________________/ \___________ */ +/* TACK IRQ ____________________________________/ \_______ */ +/* DTE IRQ __________________________________________/ \_ */ +/* AL IRQ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */ +/* _______________________________________________ */ +/* BUSY __/ \_ */ +/* */ +enum sh_mobile_i2c_op {OP_START =0 , -OP_TX_ONLY , +OP_TX_FIRST , +OP_TX ,OP_TX_STOP ,OP_TX_TO_RX , -OP_RX_ONLY , +OP_RX ,OP_RX_STOP , +OP_RX_STOP_DATA , };struct sh_mobile_i2c_data {
@@ -127,25 +198,34 @@ static unsigned char i2c_op(struct sh_mospin_lock_irqsave (&pd->lock, flags);switch (op) { -case OP_START : +case OP_START :/* issue start and trigger DTE interrupt */ iowrite8 (0x94 ,ICCR (pd));break ; -case OP_TX_ONLY : +case OP_TX_FIRST :/* disable DTE interrupt and write data */ +iowrite8 (ICIC_WAITE |ICIC_ALE |ICIC_TACKE ,ICIC (pd));iowrite8 (data,ICDR (pd));break ; -case OP_TX_STOP : +case OP_TX :/* write data */ iowrite8 (data,ICDR (pd)); -iowrite8 (0x90 ,ICCR (pd)); -iowrite8 (ICIC_ALE |ICIC_TACKE ,ICIC (pd));break ; -case OP_TX_TO_RX : +case OP_TX_STOP :/* write data and issue a stop afterwards */ iowrite8 (data,ICDR (pd)); +iowrite8 (0x90 ,ICCR (pd)); +break ; +case OP_TX_TO_RX :/* select read mode */ iowrite8 (0x81 ,ICCR (pd));break ; -case OP_RX_ONLY : +case OP_RX :/* just read data */ ret =ioread8 (ICDR (pd));break ; -case OP_RX_STOP : +case OP_RX_STOP :/* enable DTE interrupt, issue stop */ +iowrite8 (ICIC_DTEE |ICIC_WAITE |ICIC_ALE |ICIC_TACKE , +ICIC (pd)); +iowrite8 (0xc0 ,ICCR (pd)); +break ; +case OP_RX_STOP_DATA :/* enable DTE interrupt, read data, issue stop */ +iowrite8 (ICIC_DTEE |ICIC_WAITE |ICIC_ALE |ICIC_TACKE , +ICIC (pd)); ret =ioread8 (ICDR (pd));iowrite8 (0xc0 ,ICCR (pd));break ;
@@ -157,58 +237,120 @@ static unsigned char i2c_op(struct sh_moreturn ret; } +static int sh_mobile_i2c_is_first_byte (struct sh_mobile_i2c_data *pd) +{ +if (pd->pos ==-1 ) +return 1 ; + +return 0 ; +} + +static int sh_mobile_i2c_is_last_byte (struct sh_mobile_i2c_data *pd) +{ +if (pd->pos == (pd->msg->len -1 )) +return 1 ; + +return 0 ; +} + +static void sh_mobile_i2c_get_data (struct sh_mobile_i2c_data *pd, +unsigned char *buf) +{ +switch (pd->pos) { +case -1 : + *buf = (pd->msg->addr &0x7f ) <<1 ; + *buf |= (pd->msg->flags &I2C_M_RD ) ?1 :0 ; +break ; +default : + *buf = pd->msg->buf[pd->pos]; + } +} + +static int sh_mobile_i2c_isr_tx (struct sh_mobile_i2c_data *pd) +{ +unsigned char data; + +if (pd->pos == pd->msg->len) +return 1 ; + +sh_mobile_i2c_get_data (pd, &data); + +if (sh_mobile_i2c_is_last_byte (pd)) +i2c_op (pd,OP_TX_STOP , data); +else if (sh_mobile_i2c_is_first_byte (pd)) +i2c_op (pd,OP_TX_FIRST , data); +else +i2c_op (pd,OP_TX , data); + + pd->pos++; +return 0 ; +} + +static int sh_mobile_i2c_isr_rx (struct sh_mobile_i2c_data *pd) +{ +unsigned char data; +int real_pos; + +do { +if (pd->pos <=-1 ) { +sh_mobile_i2c_get_data (pd, &data); + +if (sh_mobile_i2c_is_first_byte (pd)) +i2c_op (pd,OP_TX_FIRST , data); +else +i2c_op (pd,OP_TX , data); +break ; + } + +if (pd->pos ==0 ) { +i2c_op (pd,OP_TX_TO_RX ,0 ); +break ; + } + + real_pos = pd->pos -2 ; + +if (pd->pos == pd->msg->len) { +if (real_pos <0 ) { +i2c_op (pd,OP_RX_STOP ,0 ); +break ; + } + data =i2c_op (pd,OP_RX_STOP_DATA ,0 ); + }else + data =i2c_op (pd,OP_RX ,0 ); + + pd->msg->buf[real_pos] = data; + }while (0 ); + + pd->pos++; +return pd->pos == (pd->msg->len +2 ); +} +static irqreturn_t sh_mobile_i2c_isr (int irq,void *dev_id) {struct platform_device *dev = dev_id;struct sh_mobile_i2c_data *pd =platform_get_drvdata (dev); -struct i2c_msg *msg = pd->msg; -unsigned char data, sr; -int wakeup =0 ; +unsigned char sr; +int wakeup; sr =ioread8 (ICSR (pd)); - pd->sr |= sr; + pd->sr |= sr;/* remember state */ dev_dbg (pd->dev,"i2c_isr 0x%02x 0x%02x %s %d %d!\n" , sr, pd->sr, - (msg->flags &I2C_M_RD ) ?"read" :"write" , - pd->pos, msg->len); + (pd->msg->flags &I2C_M_RD ) ?"read" :"write" , + pd->pos, pd->msg->len);if (sr & (ICSR_AL |ICSR_TACK )) { -iowrite8 (0 ,ICIC (pd));/* disable interrupts */ - wakeup =1 ; - goto do_wakeup; - } - -if (pd->pos == msg->len) { -i2c_op (pd,OP_RX_ONLY ,0 ); - wakeup =1 ; - goto do_wakeup; - } - -if (pd->pos ==-1 ) { - data = (msg->addr &0x7f ) <<1 ; - data |= (msg->flags &I2C_M_RD ) ?1 :0 ; - }else - data = msg->buf[pd->pos]; - -if ((pd->pos ==-1 ) || !(msg->flags &I2C_M_RD )) { -if (msg->flags &I2C_M_RD ) -i2c_op (pd,OP_TX_TO_RX , data); -else if (pd->pos == (msg->len -1 )) { -i2c_op (pd,OP_TX_STOP , data); - wakeup =1 ; - }else -i2c_op (pd,OP_TX_ONLY , data); - }else { -if (pd->pos == (msg->len -1 )) - data =i2c_op (pd,OP_RX_STOP ,0 ); -else - data =i2c_op (pd,OP_RX_ONLY ,0 ); +/* don't interrupt transaction - continue to issue stop */ +iowrite8 (sr & ~(ICSR_AL |ICSR_TACK ),ICSR (pd)); + wakeup =0 ; + }else if (pd->msg->flags &I2C_M_RD ) + wakeup =sh_mobile_i2c_isr_rx (pd); +else + wakeup =sh_mobile_i2c_isr_tx (pd); - msg->buf[pd->pos] = data; - } - pd->pos++; +if (sr &ICSR_WAIT )/* TODO: add delay here to support slow acks */ +iowrite8 (sr & ~ICSR_WAIT ,ICSR (pd)); - do_wakeup:if (wakeup) { pd->sr |=SW_DONE ;wake_up (&pd->wait);
@@ -219,6 +361,11 @@ static irqreturn_t sh_mobile_i2c_isr(intstatic int start_ch (struct sh_mobile_i2c_data *pd,struct i2c_msg *usr_msg) { +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 */ iowrite8 (ioread8 (ICCR (pd)) & ~ICCR_ICE ,ICCR (pd));
@@ -233,9 +380,8 @@ static int start_ch(struct sh_mobile_i2c pd->pos =-1 ; pd->sr =0 ; -/* Enable all interrupts except wait */ -iowrite8 (ioread8 (ICIC (pd)) |ICIC_ALE |ICIC_TACKE |ICIC_DTEE , -ICIC (pd)); +/* Enable all interrupts to begin with */ +iowrite8 (ICIC_WAITE |ICIC_ALE |ICIC_TACKE |ICIC_DTEE ,ICIC (pd));return 0 ; }
@@ -268,25 +414,18 @@ static int sh_mobile_i2c_xfer(struct i2cif (!k)dev_err (pd->dev,"Transfer request timed out\n" ); - retry_count =10 ; + retry_count =1000 ; again: val =ioread8 (ICSR (pd));dev_dbg (pd->dev,"val 0x%02x pd->sr 0x%02x\n" , val, pd->sr); -if ((val | pd->sr) & (ICSR_TACK |ICSR_AL )) { - err = -EIO ; -break ; - } -/* the interrupt handler may wake us up before the * transfer is finished, so poll the hardware * until we're done. */ - -if (!(!(val &ICSR_BUSY ) && (val &ICSR_SCLM ) && - (val &ICSR_SDAM ))) { -msleep (1 ); +if (val &ICSR_BUSY ) { +udelay (10 );if (retry_count--) goto again;
@@ -294,6 +433,12 @@ again:dev_err (pd->dev,"Polling timed out\n" );break ; } + +/* handle missing acknowledge and arbitration lost */ +if ((val | pd->sr) & (ICSR_TACK |ICSR_AL )) { + err = -EIO ; +break ; + } }deactivate_ch (pd);
Hi Magnus,
Quoted 11 lines · Magnus Damm
From: Magnus Damm <[email protected]>
This patch teaches the i2c-sh_mobile driver to make use of wait irqs. Without this patch only dte irqs are used which may lead to overruns and cases of missing stop and extra bytes being read on the i2c bus.
Use of wait irqs forces the hardware to pause and wait until the cpu is ready. Polling is also reworked in this patch to fix ms delay issues.
Verified with bus analyzer and tested on MigoR and AP325RXA boards.
Quoted 8 lines
Signed-off-by Magnus Damm <[email protected]>---
Paul, can you merge this for 2.6.27?
drivers/i2c/busses/i2c-sh_mobile.c | 271 +++++++++++++++++++++++++++--------- 1 file changed, 208 insertions(+), 63 deletions(-)
I'm not sure why I am Cc'd on this but Ben Dooks isn't. Ben is the one in charge of i2c bus drivers for embedded system. I'm adding him.
Quoted 359 lines
drivers/i2c/busses/i2c-sh_mobile.c +208
-63@@ -31,13 +31,84 @@#include <linux/clk.h> #include <linux/io.h> +/* Transmit operation: */ +/* */ +/* 0 byte transmit */ +/* 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 */ +/* 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 */ +/* IRQ: DTE WAIT WAIT WAIT */ +/* ICIC: -DTE */ +/* ICCR: 0x94 0x90 */ +/* ICDR: A8 D8(1) D8(2) */ +/* */ +/* 3 bytes or more, +---------+ gets repeated */ +/* */ +/* */ +/* Receive operation: */ +/* */ +/* 0 byte receive - not supported since slave may hold SDA low */ +/* */ +/* 1 byte receive [TX] | [RX] */ +/* 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 */ +/* IRQ: DTE WAIT | WAIT WAIT DTE */ +/* ICIC: -DTE | +DTE */ +/* ICCR: 0x94 0x81 | 0xc0 */ +/* ICDR: A8 | D8(1) D8(2) */ +/* */ +/* 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 */ +/* ICCR: 0x94 0x81 | 0xc0 */ +/* ICDR: A8 | D8(1) D8(2) D8(3) */ +/* */ +/* 4 bytes or more, this part is repeated +---------+ */ +/* */ +/* */ +/* Interrupt order and BUSY flag */ +/* ___ _ */ +/* SDA ___\___XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXAAAAAAAAA___/ */ +/* SCL \_/1\_/2\_/3\_/4\_/5\_/6\_/7\_/8\___/9\_____/ */ +/* */ +/* S D7 D6 D5 D4 D3 D2 D1 D0 P */ +/* ___ */ +/* WAIT IRQ ________________________________/ \___________ */ +/* TACK IRQ ____________________________________/ \_______ */ +/* DTE IRQ __________________________________________/ \_ */ +/* AL IRQ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */ +/* _______________________________________________ */ +/* BUSY __/ \_ */ +/* */ +enum sh_mobile_i2c_op {OP_START =0 , -OP_TX_ONLY , +OP_TX_FIRST , +OP_TX ,OP_TX_STOP ,OP_TX_TO_RX , -OP_RX_ONLY , +OP_RX ,OP_RX_STOP , +OP_RX_STOP_DATA , };struct sh_mobile_i2c_data {@@ -127,25 +198,34 @@ static unsigned char i2c_op(struct sh_mospin_lock_irqsave (&pd->lock, flags);switch (op) { -case OP_START : +case OP_START :/* issue start and trigger DTE interrupt */ iowrite8 (0x94 ,ICCR (pd));break ; -case OP_TX_ONLY : +case OP_TX_FIRST :/* disable DTE interrupt and write data */ +iowrite8 (ICIC_WAITE |ICIC_ALE |ICIC_TACKE ,ICIC (pd));iowrite8 (data,ICDR (pd));break ; -case OP_TX_STOP : +case OP_TX :/* write data */ iowrite8 (data,ICDR (pd)); -iowrite8 (0x90 ,ICCR (pd)); -iowrite8 (ICIC_ALE |ICIC_TACKE ,ICIC (pd));break ; -case OP_TX_TO_RX : +case OP_TX_STOP :/* write data and issue a stop afterwards */ iowrite8 (data,ICDR (pd)); +iowrite8 (0x90 ,ICCR (pd)); +break ; +case OP_TX_TO_RX :/* select read mode */ iowrite8 (0x81 ,ICCR (pd));break ; -case OP_RX_ONLY : +case OP_RX :/* just read data */ ret =ioread8 (ICDR (pd));break ; -case OP_RX_STOP : +case OP_RX_STOP :/* enable DTE interrupt, issue stop */ +iowrite8 (ICIC_DTEE |ICIC_WAITE |ICIC_ALE |ICIC_TACKE , +ICIC (pd)); +iowrite8 (0xc0 ,ICCR (pd)); +break ; +case OP_RX_STOP_DATA :/* enable DTE interrupt, read data, issue stop */ +iowrite8 (ICIC_DTEE |ICIC_WAITE |ICIC_ALE |ICIC_TACKE , +ICIC (pd)); ret =ioread8 (ICDR (pd));iowrite8 (0xc0 ,ICCR (pd));break ;@@ -157,58 +237,120 @@ static unsigned char i2c_op(struct sh_moreturn ret; } +static int sh_mobile_i2c_is_first_byte (struct sh_mobile_i2c_data *pd) +{ +if (pd->pos ==-1 ) +return 1 ; + +return 0 ; +} + +static int sh_mobile_i2c_is_last_byte (struct sh_mobile_i2c_data *pd) +{ +if (pd->pos == (pd->msg->len -1 )) +return 1 ; + +return 0 ; +} + +static void sh_mobile_i2c_get_data (struct sh_mobile_i2c_data *pd, +unsigned char *buf) +{ +switch (pd->pos) { +case -1 : + *buf = (pd->msg->addr &0x7f ) <<1 ; + *buf |= (pd->msg->flags &I2C_M_RD ) ?1 :0 ; +break ; +default : + *buf = pd->msg->buf[pd->pos]; + } +} + +static int sh_mobile_i2c_isr_tx (struct sh_mobile_i2c_data *pd) +{ +unsigned char data; + +if (pd->pos == pd->msg->len) +return 1 ; + +sh_mobile_i2c_get_data (pd, &data); + +if (sh_mobile_i2c_is_last_byte (pd)) +i2c_op (pd,OP_TX_STOP , data); +else if (sh_mobile_i2c_is_first_byte (pd)) +i2c_op (pd,OP_TX_FIRST , data); +else +i2c_op (pd,OP_TX , data); + + pd->pos++; +return 0 ; +} + +static int sh_mobile_i2c_isr_rx (struct sh_mobile_i2c_data *pd) +{ +unsigned char data; +int real_pos; + +do { +if (pd->pos <=-1 ) { +sh_mobile_i2c_get_data (pd, &data); + +if (sh_mobile_i2c_is_first_byte (pd)) +i2c_op (pd,OP_TX_FIRST , data); +else +i2c_op (pd,OP_TX , data); +break ; + } + +if (pd->pos ==0 ) { +i2c_op (pd,OP_TX_TO_RX ,0 ); +break ; + } + + real_pos = pd->pos -2 ; + +if (pd->pos == pd->msg->len) { +if (real_pos <0 ) { +i2c_op (pd,OP_RX_STOP ,0 ); +break ; + } + data =i2c_op (pd,OP_RX_STOP_DATA ,0 ); + }else + data =i2c_op (pd,OP_RX ,0 ); + + pd->msg->buf[real_pos] = data; + }while (0 ); + + pd->pos++; +return pd->pos == (pd->msg->len +2 ); +} +static irqreturn_t sh_mobile_i2c_isr (int irq,void *dev_id) {struct platform_device *dev = dev_id;struct sh_mobile_i2c_data *pd =platform_get_drvdata (dev); -struct i2c_msg *msg = pd->msg; -unsigned char data, sr; -int wakeup =0 ; +unsigned char sr; +int wakeup; sr =ioread8 (ICSR (pd)); - pd->sr |= sr; + pd->sr |= sr;/* remember state */ dev_dbg (pd->dev,"i2c_isr 0x%02x 0x%02x %s %d %d!\n" , sr, pd->sr, - (msg->flags &I2C_M_RD ) ?"read" :"write" , - pd->pos, msg->len); + (pd->msg->flags &I2C_M_RD ) ?"read" :"write" , + pd->pos, pd->msg->len);if (sr & (ICSR_AL |ICSR_TACK )) { -iowrite8 (0 ,ICIC (pd));/* disable interrupts */ - wakeup =1 ; - goto do_wakeup; - } - -if (pd->pos == msg->len) { -i2c_op (pd,OP_RX_ONLY ,0 ); - wakeup =1 ; - goto do_wakeup; - } - -if (pd->pos ==-1 ) { - data = (msg->addr &0x7f ) <<1 ; - data |= (msg->flags &I2C_M_RD ) ?1 :0 ; - }else - data = msg->buf[pd->pos]; - -if ((pd->pos ==-1 ) || !(msg->flags &I2C_M_RD )) { -if (msg->flags &I2C_M_RD ) -i2c_op (pd,OP_TX_TO_RX , data); -else if (pd->pos == (msg->len -1 )) { -i2c_op (pd,OP_TX_STOP , data); - wakeup =1 ; - }else -i2c_op (pd,OP_TX_ONLY , data); - }else { -if (pd->pos == (msg->len -1 )) - data =i2c_op (pd,OP_RX_STOP ,0 ); -else - data =i2c_op (pd,OP_RX_ONLY ,0 ); +/* don't interrupt transaction - continue to issue stop */ +iowrite8 (sr & ~(ICSR_AL |ICSR_TACK ),ICSR (pd)); + wakeup =0 ; + }else if (pd->msg->flags &I2C_M_RD ) + wakeup =sh_mobile_i2c_isr_rx (pd); +else + wakeup =sh_mobile_i2c_isr_tx (pd); - msg->buf[pd->pos] = data; - } - pd->pos++; +if (sr &ICSR_WAIT )/* TODO: add delay here to support slow acks */ +iowrite8 (sr & ~ICSR_WAIT ,ICSR (pd)); - do_wakeup:if (wakeup) { pd->sr |=SW_DONE ;wake_up (&pd->wait);@@ -219,6 +361,11 @@ static irqreturn_t sh_mobile_i2c_isr(intstatic int start_ch (struct sh_mobile_i2c_data *pd,struct i2c_msg *usr_msg) { +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 */ iowrite8 (ioread8 (ICCR (pd)) & ~ICCR_ICE ,ICCR (pd));@@ -233,9 +380,8 @@ static int start_ch(struct sh_mobile_i2c pd->pos =-1 ; pd->sr =0 ; -/* Enable all interrupts except wait */ -iowrite8 (ioread8 (ICIC (pd)) |ICIC_ALE |ICIC_TACKE |ICIC_DTEE , -ICIC (pd)); +/* Enable all interrupts to begin with */ +iowrite8 (ICIC_WAITE |ICIC_ALE |ICIC_TACKE |ICIC_DTEE ,ICIC (pd));return 0 ; }@@ -268,25 +414,18 @@ static int sh_mobile_i2c_xfer(struct i2cif (!k)dev_err (pd->dev,"Transfer request timed out\n" ); - retry_count =10 ; + retry_count =1000 ; again: val =ioread8 (ICSR (pd));dev_dbg (pd->dev,"val 0x%02x pd->sr 0x%02x\n" , val, pd->sr); -if ((val | pd->sr) & (ICSR_TACK |ICSR_AL )) { - err = -EIO ; -break ; - } -/* the interrupt handler may wake us up before the * transfer is finished, so poll the hardware * until we're done. */ - -if (!(!(val &ICSR_BUSY ) && (val &ICSR_SCLM ) && - (val &ICSR_SDAM ))) { -msleep (1 ); +if (val &ICSR_BUSY ) { +udelay (10 );if (retry_count--) goto again;@@ -294,6 +433,12 @@ again:dev_err (pd->dev,"Polling timed out\n" );break ; } + +/* handle missing acknowledge and arbitration lost */ +if ((val | pd->sr) & (ICSR_TACK |ICSR_AL )) { + err = -EIO ; +break ; + } }deactivate_ch (pd);
Signature · 1 line
Jean Delvare
Hi Jean,
Quoted 15 lines · Jean Delvare
Quoted 14 lines · Magnus Damm
From: Magnus Damm <[email protected]>
This patch teaches the i2c-sh_mobile driver to make use of wait irqs. Without this patch only dte irqs are used which may lead to overruns and cases of missing stop and extra bytes being read on the i2c bus.
Use of wait irqs forces the hardware to pause and wait until the cpu is ready. Polling is also reworked in this patch to fix ms delay issues.
Verified with bus analyzer and tested on MigoR and AP325RXA boards.
Signed-off-by Magnus Damm <[email protected]>---
Quoted 8 lines
Quoted 5 lines
Paul, can you merge this for 2.6.27?
drivers/i2c/busses/i2c-sh_mobile.c | 271 +++++++++++++++++++++++++++--------- 1 file changed, 208 insertions(+), 63 deletions(-)I'm not sure why I am Cc'd on this but Ben Dooks isn't. Ben is the one in charge of i2c bus drivers for embedded system. I'm adding him.
Sorry about that. My fault, I based my emails on my previous emails.
I'd like to classify this patch as bug fix, but it required quite a big rewrite since I had to change the interrupt handling which affected the main state machine. And while at it I decided to add some clarifications which added even more lines.
So I'd like to see this included in 2.6.27 if possible. It's a pretty isolated change.
Thanks for your help!
/ magnus
Quoted 11 lines · Magnus Damm
From: Magnus Damm <[email protected]>
This patch teaches the i2c-sh_mobile driver to make use of wait irqs. Without this patch only dte irqs are used which may lead to overruns and cases of missing stop and extra bytes being read on the i2c bus.
Use of wait irqs forces the hardware to pause and wait until the cpu is ready. Polling is also reworked in this patch to fix ms delay issues.
Verified with bus analyzer and tested on MigoR and AP325RXA boards.
Quoted 8 lines
Signed-off-by Magnus Damm <[email protected]>---
Paul, can you merge this for 2.6.27?
drivers/i2c/busses/i2c-sh_mobile.c | 271 +++++++++++++++++++++++++++--------- 1 file changed, 208 insertions(+), 63 deletions(-)
I'll add it to my queue if Jean and Ben don't have any issues with the patch. If the polling works as is, I would suggest that it is not 2.6.27 material, but if it's fixing operational bugs then it doesn't make sense to leave 2.6.27 in a known broken state.
Quoted 17 lines · Paul Mundt
Quoted 16 lines · Magnus Damm
From: Magnus Damm <[email protected]>
This patch teaches the i2c-sh_mobile driver to make use of wait irqs. Without this patch only dte irqs are used which may lead to overruns and cases of missing stop and extra bytes being read on the i2c bus.
Use of wait irqs forces the hardware to pause and wait until the cpu is ready. Polling is also reworked in this patch to fix ms delay issues.
Verified with bus analyzer and tested on MigoR and AP325RXA boards.
Signed-off-by Magnus Damm <[email protected]>---
Paul, can you merge this for 2.6.27?
Quoted 7 lines
Quoted 3 lines
drivers/i2c/busses/i2c-sh_mobile.c | 271 +++++++++++++++++++++++++++--------- 1 file changed, 208 insertions(+), 63 deletions(-)I'll add it to my queue if Jean and Ben don't have any issues with the patch. If the polling works as is, I would suggest that it is not 2.6.27 material, but if it's fixing operational bugs then it doesn't make sense to leave 2.6.27 in a known broken state.
No objection from me, as this is outside of my jurisdiction.
Signature · 1 line
Jean Delvare
Quoted 21 lines · Jean Delvare
Quoted 20 lines · Paul Mundt
Quoted 19 lines · Magnus Damm
From: Magnus Damm <[email protected]>
This patch teaches the i2c-sh_mobile driver to make use of wait irqs. Without this patch only dte irqs are used which may lead to overruns and cases of missing stop and extra bytes being read on the i2c bus.
Use of wait irqs forces the hardware to pause and wait until the cpu is ready. Polling is also reworked in this patch to fix ms delay issues.
Verified with bus analyzer and tested on MigoR and AP325RXA boards.
Signed-off-by Magnus Damm <[email protected]>---
Paul, can you merge this for 2.6.27?
drivers/i2c/busses/i2c-sh_mobile.c | 271 +++++++++++++++++++++++++++--------- 1 file changed, 208 insertions(+), 63 deletions(-)
Quoted 7 lines
Quoted 4 lines
I'll add it to my queue if Jean and Ben don't have any issues with the patch. If the polling works as is, I would suggest that it is not 2.6.27 material, but if it's fixing operational bugs then it doesn't make sense to leave 2.6.27 in a known broken state.
No objection from me, as this is outside of my jurisdiction.
Well, it's been a week and there's been no comments, so I'll roll this in to my queue.