Author: manu
Date: Fri Jan 10 18:52:14 2020
New Revision: 356609
URL: https://svnweb.freebsd.org/changeset/base/356609

Log:
  twsi: Rework how we handle the i2c messages
  
  We use to handle each message separately in i2c_transfer but that cannot
  work with message with NOSTOP as it confuses the controller that we disable
  the interrupts and start a new message.
  Handle every message in the interrupt handler and fire a new start condition
  if the previous message have NOSTOP, the controller understand this as a
  repeated start.
  This fixes booting on Allwinner A10/A20 platform where before the i2c 
controller
  used to write 0 to the PMIC register that control the regulators as it though 
that
  this was the continuation of the write message.
  
  Tested on:   A20 BananaPi, Cubieboard 1 (kevans)
  Reported by:  kevans
  MFC after:    1 month

Modified:
  head/sys/dev/iicbus/twsi/twsi.c
  head/sys/dev/iicbus/twsi/twsi.h

Modified: head/sys/dev/iicbus/twsi/twsi.c
==============================================================================
--- head/sys/dev/iicbus/twsi/twsi.c     Fri Jan 10 18:48:11 2020        
(r356608)
+++ head/sys/dev/iicbus/twsi/twsi.c     Fri Jan 10 18:52:14 2020        
(r356609)
@@ -481,7 +481,6 @@ static int
 twsi_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs)
 {
        struct twsi_softc *sc;
-       int i;
 
        sc = device_get_softc(dev);
 
@@ -495,28 +494,25 @@ twsi_transfer(device_t dev, struct iic_msg *msgs, uint
        TWSI_WRITE(sc, sc->reg_control, sc->control_val);
        debugf(dev, "transmitting %d messages\n", nmsgs);
        debugf(sc->dev, "status=%x\n", TWSI_READ(sc, sc->reg_status));
-       for (i = 0; i < nmsgs && sc->error == 0; i++) {
-               sc->transfer = 1;
-               sc->msg = &msgs[i];
-               debugf(dev, "msg[%d] flags: %x\n", i, msgs[i].flags);
-               debugf(dev, "msg[%d] len: %d\n", i, msgs[i].len);
+       sc->nmsgs = nmsgs;
+       sc->msgs = msgs;
+       sc->msg_idx = 0;
+       sc->transfer = 1;
 
-               /* Send start and re-enable interrupts */
-               sc->control_val = TWSI_CONTROL_TWSIEN |
-                       TWSI_CONTROL_INTEN | TWSI_CONTROL_ACK;
-               if (sc->msg->len == 1)
-                       sc->control_val &= ~TWSI_CONTROL_ACK;
-               TWSI_WRITE(sc, sc->reg_control, sc->control_val | 
TWSI_CONTROL_START);
-               while (sc->error == 0 && sc->transfer != 0) {
-                       pause_sbt("twsi", SBT_1MS * 30, SBT_1MS, 0);
-               }
+       /* Send start and re-enable interrupts */
+       sc->control_val = TWSI_CONTROL_TWSIEN |
+               TWSI_CONTROL_INTEN | TWSI_CONTROL_ACK;
+       if (sc->msgs[0].len == 1)
+               sc->control_val &= ~TWSI_CONTROL_ACK;
+       TWSI_WRITE(sc, sc->reg_control, sc->control_val | TWSI_CONTROL_START);
+       while (sc->error == 0 && sc->transfer != 0) {
+               pause_sbt("twsi", SBT_1MS * 30, SBT_1MS, 0);
+       }
+       debugf(sc->dev, "pause finish\n");
 
-               debugf(dev, "Done with msg[%d]\n", i);
-               if (sc->error) {
-                       debugf(sc->dev, "Error, aborting (%d)\n", sc->error);
-                       TWSI_WRITE(sc, sc->reg_control, 0);
-                       goto out;
-               }
+       if (sc->error) {
+               debugf(sc->dev, "Error, aborting (%d)\n", sc->error);
+               TWSI_WRITE(sc, sc->reg_control, 0);
        }
 
        /* Disable module and interrupts */
@@ -524,7 +520,6 @@ twsi_transfer(device_t dev, struct iic_msg *msgs, uint
        TWSI_WRITE(sc, sc->reg_control, 0);
        debugf(sc->dev, "status=%x\n", TWSI_READ(sc, sc->reg_status));
 
-out:
        return (sc->error);
 }
 
@@ -537,122 +532,123 @@ twsi_intr(void *arg)
 
        sc = arg;
 
-       debugf(sc->dev, "Got interrupt\n");
+       debugf(sc->dev, "Got interrupt Current msg=%x\n", sc->msg_idx);
 
-       while (TWSI_READ(sc, sc->reg_control) & TWSI_CONTROL_IFLG) {
-               status = TWSI_READ(sc, sc->reg_status);
-               debugf(sc->dev, "status=%x\n", status);
+       status = TWSI_READ(sc, sc->reg_status);
+       debugf(sc->dev, "initial status=%x\n", status);
 
-               switch (status) {
-               case TWSI_STATUS_START:
-               case TWSI_STATUS_RPTD_START:
-                       /* Transmit the address */
-                       debugf(sc->dev, "Send the address\n");
+       switch (status) {
+       case TWSI_STATUS_START:
+       case TWSI_STATUS_RPTD_START:
+               /* Transmit the address */
+               debugf(sc->dev, "Send the address\n");
 
-                       if (sc->msg->flags & IIC_M_RD)
-                               TWSI_WRITE(sc, sc->reg_data,
-                                   sc->msg->slave | LSB);
-                       else
-                               TWSI_WRITE(sc, sc->reg_data,
-                                   sc->msg->slave & ~LSB);
+               if (sc->msgs[sc->msg_idx].flags & IIC_M_RD)
+                       TWSI_WRITE(sc, sc->reg_data,
+                           sc->msgs[sc->msg_idx].slave | LSB);
+               else
+                       TWSI_WRITE(sc, sc->reg_data,
+                           sc->msgs[sc->msg_idx].slave & ~LSB);
+               TWSI_WRITE(sc, sc->reg_control, sc->control_val);
+               break;
 
-                       TWSI_WRITE(sc, sc->reg_control, sc->control_val);
-                       break;
+       case TWSI_STATUS_ADDR_W_ACK:
+               debugf(sc->dev, "Ack received after transmitting the address 
(write)\n");
+               /* Directly send the first byte */
+               sc->sent_bytes = 0;
+               debugf(sc->dev, "Sending byte 0 = %x\n", 
sc->msgs[sc->msg_idx].buf[0]);
+               TWSI_WRITE(sc, sc->reg_data, sc->msgs[sc->msg_idx].buf[0]);
 
-               case TWSI_STATUS_ADDR_W_ACK:
-                       debugf(sc->dev, "Ack received after transmitting the 
address\n");
-                       /* Directly send the first byte */
-                       sc->sent_bytes = 0;
-                       debugf(sc->dev, "Sending byte 0 = %x\n", 
sc->msg->buf[0]);
-                       TWSI_WRITE(sc, sc->reg_data, sc->msg->buf[0]);
+               TWSI_WRITE(sc, sc->reg_control, sc->control_val);
+               break;
 
-                       TWSI_WRITE(sc, sc->reg_control, sc->control_val);
-                       break;
+       case TWSI_STATUS_ADDR_R_ACK:
+               debugf(sc->dev, "Ack received after transmitting the address 
(read)\n");
+               sc->recv_bytes = 0;
 
-               case TWSI_STATUS_ADDR_R_ACK:
-                       debugf(sc->dev, "Ack received after transmitting the 
address\n");
-                       sc->recv_bytes = 0;
+               TWSI_WRITE(sc, sc->reg_control, sc->control_val);
+               break;
 
-                       TWSI_WRITE(sc, sc->reg_control, sc->control_val);
-                       break;
+       case TWSI_STATUS_ADDR_W_NACK:
+       case TWSI_STATUS_ADDR_R_NACK:
+               debugf(sc->dev, "No ack received after transmitting the 
address\n");
+               sc->transfer = 0;
+               sc->error = ETIMEDOUT;
+               sc->control_val = 0;
+               wakeup(sc);
+               break;
 
-               case TWSI_STATUS_ADDR_W_NACK:
-               case TWSI_STATUS_ADDR_R_NACK:
-                       debugf(sc->dev, "No ack received after transmitting the 
address\n");
-                       sc->transfer = 0;
-                       sc->error = ETIMEDOUT;
-                       sc->control_val = 0;
-                       wakeup(sc);
-                       break;
-
-               case TWSI_STATUS_DATA_WR_ACK:
-                       debugf(sc->dev, "Ack received after transmitting 
data\n");
-                       if (sc->sent_bytes++ == (sc->msg->len - 1)) {
-                               debugf(sc->dev, "Done sending all the bytes\n");
-                               /* Send stop, no interrupts on stop */
-                               if (!(sc->msg->flags & IIC_M_NOSTOP)) {
-                                       debugf(sc->dev, "Done TX data, send 
stop\n");
-                                       TWSI_WRITE(sc, sc->reg_control,
-                                         sc->control_val | TWSI_CONTROL_STOP);
-                               } else {
-                                       sc->control_val &= ~TWSI_CONTROL_INTEN;
-                                       TWSI_WRITE(sc, sc->reg_control,
-                                           sc->control_val);
-                               }
-                               transfer_done = 1;
-                       } else {
-                               debugf(sc->dev, "Sending byte %d = %x\n",
-                                   sc->sent_bytes,
-                                   sc->msg->buf[sc->sent_bytes]);
-                               TWSI_WRITE(sc, sc->reg_data,
-                                   sc->msg->buf[sc->sent_bytes]);
+       case TWSI_STATUS_DATA_WR_ACK:
+               debugf(sc->dev, "Ack received after transmitting data\n");
+               if (sc->sent_bytes++ == (sc->msgs[sc->msg_idx].len - 1)) {
+                       debugf(sc->dev, "Done sending all the bytes for msg 
%d\n", sc->msg_idx);
+                       /* Send stop, no interrupts on stop */
+                       if (!(sc->msgs[sc->msg_idx].flags & IIC_M_NOSTOP)) {
+                               debugf(sc->dev, "Done TX data, send stop\n");
                                TWSI_WRITE(sc, sc->reg_control,
-                                   sc->control_val);
+                                   sc->control_val | TWSI_CONTROL_STOP);
+                       } else {
+                               debugf(sc->dev, "Done TX data with NO_STOP\n");
+                               TWSI_WRITE(sc, sc->reg_control, sc->control_val 
| TWSI_CONTROL_START);
                        }
+                       sc->msg_idx++;
+                       if (sc->msg_idx == sc->nmsgs) {
+                               debugf(sc->dev, "transfer_done=1\n");
+                               transfer_done = 1;
+                       }
+               } else {
+                       debugf(sc->dev, "Sending byte %d = %x\n",
+                           sc->sent_bytes,
+                           sc->msgs[sc->msg_idx].buf[sc->sent_bytes]);
+                       TWSI_WRITE(sc, sc->reg_data,
+                           sc->msgs[sc->msg_idx].buf[sc->sent_bytes]);
+                       TWSI_WRITE(sc, sc->reg_control,
+                           sc->control_val);
+               }
+               break;
 
-                       break;
-               case TWSI_STATUS_DATA_RD_ACK:
-                       debugf(sc->dev, "Ack received after receiving data\n");
-                       debugf(sc->dev, "msg_len=%d recv_bytes=%d\n", 
sc->msg->len, sc->recv_bytes);
-                       sc->msg->buf[sc->recv_bytes++] = TWSI_READ(sc, 
sc->reg_data);
+       case TWSI_STATUS_DATA_RD_ACK:
+               debugf(sc->dev, "Ack received after receiving data\n");
+               debugf(sc->dev, "msg_len=%d recv_bytes=%d\n", 
sc->msgs[sc->msg_idx].len, sc->recv_bytes);
+               sc->msgs[sc->msg_idx].buf[sc->recv_bytes++] = TWSI_READ(sc, 
sc->reg_data);
 
-                       /* If we only have one byte left, disable ACK */
-                       if (sc->msg->len - sc->recv_bytes == 1)
-                               sc->control_val &= ~TWSI_CONTROL_ACK;
-                       TWSI_WRITE(sc, sc->reg_control, sc->control_val);
-                       break;
+               /* If we only have one byte left, disable ACK */
+               if (sc->msgs[sc->msg_idx].len - sc->recv_bytes == 1)
+                       sc->control_val &= ~TWSI_CONTROL_ACK;
+               if (sc->msgs[sc->msg_idx].len - sc->recv_bytes) {
+                       sc->msg_idx++;
+                       if (sc->msg_idx == sc->nmsgs - 1)
+                               transfer_done = 1;
+               }
+               TWSI_WRITE(sc, sc->reg_control, sc->control_val);
+               break;
 
-               case TWSI_STATUS_DATA_RD_NOACK:
-                       if (sc->msg->len - sc->recv_bytes == 1) {
-                               sc->msg->buf[sc->recv_bytes++] = TWSI_READ(sc, 
sc->reg_data);
-                               debugf(sc->dev, "Done RX data, send stop 
(2)\n");
-                               if (!(sc->msg->flags & IIC_M_NOSTOP))
-                                       TWSI_WRITE(sc, sc->reg_control,
-                                         sc->control_val | TWSI_CONTROL_STOP);
-                       } else {
-                               debugf(sc->dev, "No ack when receiving data\n");
-                               sc->error = ENXIO;
-                               sc->control_val = 0;
-                       }
-                       sc->transfer = 0;
-                       transfer_done = 1;
-                       break;
-
-               default:
-                       debugf(sc->dev, "status=%x hot handled\n", status);
-                       sc->transfer = 0;
+       case TWSI_STATUS_DATA_RD_NOACK:
+               if (sc->msgs[sc->msg_idx].len - sc->recv_bytes == 1) {
+                       sc->msgs[sc->msg_idx].buf[sc->recv_bytes++] = 
TWSI_READ(sc, sc->reg_data);
+                       debugf(sc->dev, "Done RX data, send stop (2)\n");
+                       if (!(sc->msgs[sc->msg_idx].flags & IIC_M_NOSTOP))
+                               TWSI_WRITE(sc, sc->reg_control,
+                                   sc->control_val | TWSI_CONTROL_STOP);
+               } else {
+                       debugf(sc->dev, "No ack when receiving data\n");
                        sc->error = ENXIO;
                        sc->control_val = 0;
-                       wakeup(sc);
-                       break;
                }
+               sc->transfer = 0;
+               transfer_done = 1;
+               break;
 
-               if (sc->need_ack)
-                       TWSI_WRITE(sc, sc->reg_control,
-                           sc->control_val | TWSI_CONTROL_IFLG);
+       default:
+               debugf(sc->dev, "status=%x hot handled\n", status);
+               sc->transfer = 0;
+               sc->error = ENXIO;
+               sc->control_val = 0;
+               wakeup(sc);
+               break;
        }
 
-       debugf(sc->dev, "Done with interrupts\n");
+       debugf(sc->dev, "Done with interrupts\n\n");
        if (transfer_done == 1) {
                sc->transfer = 0;
                wakeup(sc);

Modified: head/sys/dev/iicbus/twsi/twsi.h
==============================================================================
--- head/sys/dev/iicbus/twsi/twsi.h     Fri Jan 10 18:48:11 2020        
(r356608)
+++ head/sys/dev/iicbus/twsi/twsi.h     Fri Jan 10 18:52:14 2020        
(r356609)
@@ -57,7 +57,9 @@ struct twsi_softc {
        void *                  intrhand;
        bool                    have_intr;
 
-       struct iic_msg          *msg;
+       struct iic_msg          *msgs;
+       uint32_t                nmsgs;
+       uint32_t                msg_idx;
        uint16_t                sent_bytes;
        uint16_t                recv_bytes;
        int                     transfer;
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to