AnsweredAssumed Answered

Question on using MPC8306 as i2c slave

Question asked by Hook Guo on Sep 12, 2013

Hi buddies,

 

I'm developing an i2c slave driver using linux 2.6.34 on MPC8306.

In my test, I use a RS232-to-I2C adapter connected to PC as the i2c master.

When doing master receive and slave transmit, when the last byte has been xfered,

the slave always pull the I2C_SDA low and doesn't release the i2c bus,

STOP can't be received. The datasheet seems doesn't make it clear that how to

release I2C_SDA. Is there anybody facing the same issue?

Please help, thanks in advance.

 

As the i2c protocol says, no ACK on the last byte transmit before STOP, and I found

I2CnSR register RXAK bit set, which mean no ACK is received. So, I think the slave

hang the bus and waiting for ACK from master.

 

Below is my i2c slave driver interrupt routine.

 

static irqreturn_t fsl_i2cli_irq_handler(int irq, void *device_id)
{
    struct fsl_i2cli_priv *i2cli = device_id;

    if (i2cli != fsl_i2cli)
        return IRQ_HANDLED;

    if ((readb(i2cli->base + MPC_I2C2SR) & I2C2SR_MIF) == 0)
        return IRQ_HANDLED;

    /* If we are addressed as slave, check I2CnSR[SRW] and
       set I2CnCR[MTX] accordingly */
    if (readb(i2cli->base + MPC_I2C2SR) & I2C2SR_MAAS) {
        int tmp;
        u8 cr, x;

        PDEBUG("#### Addressed as an i2c slave ####\n");
           
        tmp = readb(i2cli->base + MPC_I2C2SR) & I2C2SR_SRW;
        i2cli->mode = tmp > 0 ? 1 : 0;

        PDEBUG("xfer mode: %s\n", (i2cli->mode == 0 ?
                "Slave receive" : "Slave transmit"));

        cr = readb(i2cli->base + MPC_I2C2CR);
        if (i2cli->mode == 0)
            writeb((cr & ~I2C2CR_MTX), i2cli->base + MPC_I2C2CR);
        else if (i2cli->mode == 1)
            writeb((cr | I2C2CR_MTX), i2cli->base + MPC_I2C2CR);

        writeb(0, i2cli->base + MPC_I2C2SR);

        /* dummy read on first byte */
        x = readb(i2cli->base + MPC_I2C2DR);
        PDEBUG("Dummy read x: 0x%02x\n", x);
    }

    /* Set by the falling edge of the 9th clock of a byte transfer */
    if (readb(i2cli->base + MPC_I2C2SR) & I2C2SR_MCF) {
        PDEBUG("Data xfered\n");
       
        writeb(0, i2cli->base + MPC_I2C2SR);

        /* If it's ACK on Master START command, do a dummy read */
        if (!i2cli->f_ack_on_start) {
            readb(i2cli->base + MPC_I2C2DR);
            i2cli->f_ack_on_start = 1;
            goto verify_mbb;
        }
   
        /* Data xfer buffer is full */   
        if ((i2cli->mode == 0) &&
                (i2cli->buf_ptr - i2cli->buf >= I2CLI_XFER_BUF_SIZE)) {
            i2cli->xfer_done = 1;
            wake_up_interruptible(&i2cli->wait_qh);
            goto end;
        }

        /* No more data to xfer in slave transmit mode */
        if ((i2cli->mode == 1) && (i2cli->buf_ptr - i2cli->buf >= i2cli->size)) {
            i2cli->xfer_done = 1;
            wake_up_interruptible(&i2cli->wait_qh);
            goto end;
        }

        if (i2cli->mode == 0) {
            /* Slave receive */
            u8 data = readb(i2cli->base + MPC_I2C2DR);
            PDEBUG("Read data: 0x%02x\n", data);
            PDEBUG("buf: %p, buf_ptr: %p\n", i2cli->buf, i2cli->buf_ptr);
            i2cli->size++;
            *i2cli->buf_ptr++ = data;
        } else {
            /* Slave transmit */
            writeb(*i2cli->buf_ptr, i2cli->base + MPC_I2C2DR);
            PDEBUG("Write data: 0x%02x\n", *i2cli->buf_ptr);
            PDEBUG("buf: %p, buf_ptr: %p\n", i2cli->buf, i2cli->buf_ptr);
            i2cli->buf_ptr++;
        }
    }

verify_mbb:
   
    /* If a STOP condition is detected, I2CnSR[MBB] is cleared,
     * Seems only make effect in case of slave receive */
    if ((readb(i2cli->base + MPC_I2C2SR) & I2C2SR_MBB) == 0) {
        PDEBUG("Stoped\n");
        i2cli->xfer_done = 1;
        wake_up_interruptible(&i2cli->wait_qh);
    }

end:
    return IRQ_HANDLED;
}

 

Message was edited by: Jia Guo

Outcomes