예제 #1
0
파일: gxiic.c 프로젝트: ryo/netbsd-src
static int
gxiic_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, const void *vcmd,
           size_t cmdlen, void *vbuf, size_t buflen, int flags)
{
    struct gxiic_softc *sc = cookie;
    int rv = -1;

    if (I2C_OP_READ_P(op) && (cmdlen == 0) && (buflen == 1))
        rv = pxa2x0_i2c_read(&sc->sc_pxa_i2c, addr, (u_char *)vbuf);

    if ((I2C_OP_READ_P(op)) && (cmdlen == 1) && (buflen == 1)) {
        rv = pxa2x0_i2c_write(&sc->sc_pxa_i2c, addr, *(u_char *)vbuf);
        if (rv == 0)
            rv = pxa2x0_i2c_read(&sc->sc_pxa_i2c,
                                 addr, (u_char *)vbuf);
    }

    if ((I2C_OP_READ_P(op)) && (cmdlen == 1) && (buflen == 2)) {
        rv = pxa2x0_i2c_write(&sc->sc_pxa_i2c, addr, *(u_char *)vbuf);
        if (rv == 0)
            rv = pxa2x0_i2c_read(&sc->sc_pxa_i2c,
                                 addr, (u_char *)vbuf);
        if (rv == 0)
            rv = pxa2x0_i2c_read(&sc->sc_pxa_i2c,
                                 addr, (u_char *)(vbuf) + 1);
    }

    if ((I2C_OP_WRITE_P(op)) && (cmdlen == 0) && (buflen == 1))
        rv = pxa2x0_i2c_write(&sc->sc_pxa_i2c, addr, *(u_char *)vbuf);

    if ((I2C_OP_WRITE_P(op)) && (cmdlen == 1) && (buflen == 1)) {
        rv = pxa2x0_i2c_write(&sc->sc_pxa_i2c,
                              addr, *(const u_char *)vcmd);
        if (rv == 0)
            rv = pxa2x0_i2c_write(&sc->sc_pxa_i2c,
                                  addr, *(u_char *)vbuf);
    }

    if ((I2C_OP_WRITE_P(op)) && (cmdlen == 1) && (buflen == 2)) {
        rv = pxa2x0_i2c_write(&sc->sc_pxa_i2c,
                              addr, *(const u_char *)vcmd);
        if (rv == 0)
            rv = pxa2x0_i2c_write_2(&sc->sc_pxa_i2c,
                                    addr, *(u_short *)vbuf);
    }

    /* Handle quick_read/quick_write ops - XXX Untested XXX */
    if ((cmdlen == 0) && (buflen == 0))
        rv = pxa2x0_i2c_quick(&sc->sc_pxa_i2c, addr,
                              I2C_OP_READ_P(op)?1:0);

    return rv;
}
예제 #2
0
static int
nfsmb_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, const void *cmd,
	   size_t cmdlen, void *vbuf, size_t buflen, int flags)
{
	struct nfsmb_softc *sc  = (struct nfsmb_softc *)cookie;
	uint8_t *p = vbuf;
	int rv;

	if (I2C_OP_READ_P(op) && (cmdlen == 0) && (buflen == 1)) {
		rv = nfsmb_receive_1(sc, addr, op, flags);
		if (rv == -1)
			return -1;
		*p = (uint8_t)rv;
		return 0;
	}

	if ((I2C_OP_READ_P(op)) && (cmdlen == 1) && (buflen == 1)) {
		rv = nfsmb_read_1(sc, *(const uint8_t*)cmd, addr, op, flags);
		if (rv == -1)
			return -1;
		*p = (uint8_t)rv;
		return 0;
	}

	if ((I2C_OP_READ_P(op)) && (cmdlen == 1) && (buflen == 2)) {
		rv = nfsmb_read_2(sc, *(const uint8_t*)cmd, addr, op, flags);
		if (rv == -1)
			return -1;
		*(uint16_t *)p = (uint16_t)rv;
		return 0;
	}

	if ((I2C_OP_WRITE_P(op)) && (cmdlen == 0) && (buflen == 1))
		return nfsmb_send_1(sc, *(uint8_t*)vbuf, addr, op, flags);

	if ((I2C_OP_WRITE_P(op)) && (cmdlen == 1) && (buflen == 1))
		return nfsmb_write_1(sc, *(const uint8_t*)cmd, *(uint8_t*)vbuf,
		    addr, op, flags);

	if ((I2C_OP_WRITE_P(op)) && (cmdlen == 1) && (buflen == 2))
		return nfsmb_write_2(sc,
		    *(const uint8_t*)cmd, *((uint16_t *)vbuf), addr, op, flags);

	return -1;
}
예제 #3
0
static int
ichsmb_intr(void *arg)
{
	struct ichsmb_softc *sc = arg;
	uint8_t st;
	uint8_t *b;
	size_t len;
#ifdef ICHIIC_DEBUG
	char fbuf[64];
#endif

	/* Read status */
	st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LPCIB_SMB_HS);
	if ((st & LPCIB_SMB_HS_BUSY) != 0 || (st & (LPCIB_SMB_HS_INTR |
	    LPCIB_SMB_HS_DEVERR | LPCIB_SMB_HS_BUSERR | LPCIB_SMB_HS_FAILED |
	    LPCIB_SMB_HS_SMBAL | LPCIB_SMB_HS_BDONE)) == 0)
		/* Interrupt was not for us */
		return (0);

#ifdef ICHIIC_DEBUG
	snprintb(fbuf, sizeof(fbuf), LPCIB_SMB_HS_BITS, st);
	printf("%s: intr st 0x%s\n", device_xname(sc->sc_dev), fbuf);
#endif

	/* Clear status bits */
	bus_space_write_1(sc->sc_iot, sc->sc_ioh, LPCIB_SMB_HS, st);

	/* Check for errors */
	if (st & (LPCIB_SMB_HS_DEVERR | LPCIB_SMB_HS_BUSERR | LPCIB_SMB_HS_FAILED)) {
		sc->sc_i2c_xfer.error = 1;
		goto done;
	}

	if (st & LPCIB_SMB_HS_INTR) {
		if (I2C_OP_WRITE_P(sc->sc_i2c_xfer.op))
			goto done;

		/* Read data */
		b = sc->sc_i2c_xfer.buf;
		len = sc->sc_i2c_xfer.len;
		if (len > 0)
			b[0] = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
			    LPCIB_SMB_HD0);
		if (len > 1)
			b[1] = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
			    LPCIB_SMB_HD1);
	}

done:
	if ((sc->sc_i2c_xfer.flags & I2C_F_POLL) == 0)
		wakeup(sc);
	return (1);
}
예제 #4
0
static int
amdpm_smbus_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, const void *cmd,
		 size_t cmdlen, void *vbuf, size_t buflen, int flags)
{
        struct amdpm_softc *sc  = (struct amdpm_softc *) cookie;
	sc->sc_smbus_slaveaddr  = addr;
	uint8_t *p = vbuf;
	int rv;
	
	if ((cmdlen == 0) && (buflen == 0))
		return amdpm_smbus_quick(sc, op);

	if (I2C_OP_READ_P(op) && (cmdlen == 0) && (buflen == 1)) {
		rv = amdpm_smbus_receive_1(sc, op);
		if (rv == -1)
			return -1;
		*p = (uint8_t)rv;
		return 0;
	}
	
	if ((I2C_OP_READ_P(op)) && (cmdlen == 1) && (buflen == 1)) {
		rv = amdpm_smbus_read_1(sc, *(const uint8_t *)cmd, op);
		if (rv == -1)
			return -1;
		*p = (uint8_t)rv;
		return 0;
	}
	
	if ((I2C_OP_WRITE_P(op)) && (cmdlen == 0) && (buflen == 1))
		return amdpm_smbus_send_1(sc, *(uint8_t*)vbuf, op);
	
	if ((I2C_OP_WRITE_P(op)) && (cmdlen == 1) && (buflen == 1))
		return amdpm_smbus_write_1(sc,
					   *(const uint8_t*)cmd,
					   *(uint8_t*)vbuf,
					   op);
	
	return -1;  
}
예제 #5
0
파일: amdpm.c 프로젝트: SylvestreG/bitrig
int
amdpm_intr(void *arg)
{
	struct amdpm_softc *sc = arg;
	u_int16_t st, data;
	u_int8_t *b;
	size_t len;

	/* Read status */
	st = bus_space_read_2(sc->sc_iot, sc->sc_i2c_ioh, AMDPM_SMBSTAT);
	if ((st & AMDPM_SMBSTAT_HBSY) != 0 || (st & (AMDPM_SMBSTAT_ABRT |
	    AMDPM_SMBSTAT_COL | AMDPM_SMBSTAT_PRERR | AMDPM_SMBSTAT_CYC |
	    AMDPM_SMBSTAT_TO | AMDPM_SMBSTAT_SNP | AMDPM_SMBSTAT_SLV |
	    AMDPM_SMBSTAT_SMBA)) == 0)
		/* Interrupt was not for us */
		return (0);

	DPRINTF("%s: intr: st 0x%b\n", sc->sc_dev.dv_xname, st,
	    AMDPM_SMBSTAT_BITS);

	/* Clear status bits */
	bus_space_write_2(sc->sc_iot, sc->sc_i2c_ioh, AMDPM_SMBSTAT, st);

	/* Check for errors */
	if (st & (AMDPM_SMBSTAT_COL | AMDPM_SMBSTAT_PRERR |
	    AMDPM_SMBSTAT_TO)) {
		sc->sc_i2c_xfer.error = 1;
		goto done;
	}

	if (st & AMDPM_SMBSTAT_CYC) {
		if (I2C_OP_WRITE_P(sc->sc_i2c_xfer.op))
			goto done;

		/* Read data */
		b = sc->sc_i2c_xfer.buf;
		len = sc->sc_i2c_xfer.len;
		if (len > 0) {
			data = bus_space_read_2(sc->sc_iot, sc->sc_i2c_ioh,
			    AMDPM_SMBDATA);
			b[0] = data & 0xff;
		}
		if (len > 1)
			b[1] = (data >> 8) & 0xff;
	}

done:
	if ((sc->sc_i2c_xfer.flags & I2C_F_POLL) == 0)
		wakeup(sc);
	return (1);
}
예제 #6
0
static int
piixpm_intr(void *arg)
{
	struct piixpm_smbus *smbus = arg;
	struct piixpm_softc *sc = smbus->softc;
	u_int8_t st;
	u_int8_t *b;
	size_t len;

	/* Read status */
	st = bus_space_read_1(sc->sc_smb_iot, sc->sc_smb_ioh, PIIX_SMB_HS);
	if ((st & PIIX_SMB_HS_BUSY) != 0 || (st & (PIIX_SMB_HS_INTR |
	    PIIX_SMB_HS_DEVERR | PIIX_SMB_HS_BUSERR |
	    PIIX_SMB_HS_FAILED)) == 0)
		/* Interrupt was not for us */
		return (0);

	DPRINTF(("%s: intr st 0x%d\n", device_xname(sc->sc_dev), st & 0xff));

	/* Clear status bits */
	bus_space_write_1(sc->sc_smb_iot, sc->sc_smb_ioh, PIIX_SMB_HS, st);

	/* Check for errors */
	if (st & (PIIX_SMB_HS_DEVERR | PIIX_SMB_HS_BUSERR |
	    PIIX_SMB_HS_FAILED)) {
		sc->sc_i2c_xfer.error = 1;
		goto done;
	}

	if (st & PIIX_SMB_HS_INTR) {
		if (I2C_OP_WRITE_P(sc->sc_i2c_xfer.op))
			goto done;

		/* Read data */
		b = sc->sc_i2c_xfer.buf;
		len = sc->sc_i2c_xfer.len;
		if (len > 0)
			b[0] = bus_space_read_1(sc->sc_smb_iot, sc->sc_smb_ioh,
			    PIIX_SMB_HD0);
		if (len > 1)
			b[1] = bus_space_read_1(sc->sc_smb_iot, sc->sc_smb_ioh,
			    PIIX_SMB_HD1);
	}

done:
	if ((sc->sc_i2c_xfer.flags & I2C_F_POLL) == 0)
		wakeup(sc);
	return (1);
}
예제 #7
0
int
ichiic_intr(void *arg)
{
	struct ichiic_softc *sc = arg;
	u_int8_t st;
	u_int8_t *b;
	size_t len;

	/* Read status */
	st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, ICH_SMB_HS);
	if ((st & ICH_SMB_HS_BUSY) != 0 || (st & (ICH_SMB_HS_INTR |
	    ICH_SMB_HS_DEVERR | ICH_SMB_HS_BUSERR | ICH_SMB_HS_FAILED |
	    ICH_SMB_HS_SMBAL | ICH_SMB_HS_BDONE)) == 0)
		/* Interrupt was not for us */
		return (0);

	DPRINTF(("%s: intr st 0x%b\n", sc->sc_dev.dv_xname, st,
	    ICH_SMB_HS_BITS));

	/* Clear status bits */
	bus_space_write_1(sc->sc_iot, sc->sc_ioh, ICH_SMB_HS, st);

	/* Check for errors */
	if (st & (ICH_SMB_HS_DEVERR | ICH_SMB_HS_BUSERR | ICH_SMB_HS_FAILED)) {
		sc->sc_i2c_xfer.error = 1;
		goto done;
	}

	if (st & ICH_SMB_HS_INTR) {
		if (I2C_OP_WRITE_P(sc->sc_i2c_xfer.op))
			goto done;

		/* Read data */
		b = sc->sc_i2c_xfer.buf;
		len = sc->sc_i2c_xfer.len;
		if (len > 0)
			b[0] = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
			    ICH_SMB_HD0);
		if (len > 1)
			b[1] = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
			    ICH_SMB_HD1);
	}

done:
	if ((sc->sc_i2c_xfer.flags & I2C_F_POLL) == 0)
		wakeup(sc);
	return (1);
}
예제 #8
0
int
amdiic_intr(void *arg)
{
	struct amdiic_softc *sc = arg;
	int st;
	u_int8_t *b;
	size_t len;

	/* Read status */
	if ((st = amdiic_read(sc, AMD8111_SMB_STAT)) == -1)
		return (-1);
	if (st == 0)
		/* Interrupt was not for us */
		return (0);

	DPRINTF(("%s: intr: st 0x%02x\n", sc->sc_dev.dv_xname, st));

	/* Check for errors */
	if ((st & AMD8111_SMB_STAT_MASK) != 0) {
		sc->sc_i2c_xfer.error = 1;
		goto done;
	}

	if (st & AMD8111_SMB_STAT_DONE) {
		if (I2C_OP_WRITE_P(sc->sc_i2c_xfer.op))
			goto done;

		/* Read data */
		b = sc->sc_i2c_xfer.buf;
		len = sc->sc_i2c_xfer.len;
		if (len > 0)
			b[0] = amdiic_read(sc, AMD8111_SMB_DATA(0));
		if (len > 1)
			b[1] = amdiic_read(sc, AMD8111_SMB_DATA(1));
	}

done:
	if ((sc->sc_i2c_xfer.flags & I2C_F_POLL) == 0)
		wakeup(sc);
	return (1);
}
예제 #9
0
int
ichiic_i2c_exec(void *cookie, i2c_op_t op, i2c_addr_t addr,
    const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags)
{
	struct ichiic_softc *sc = cookie;
	u_int8_t *b;
	u_int8_t ctl, st;
	int retries;

	DPRINTF(("%s: exec: op %d, addr 0x%02x, cmdlen %d, len %d, "
	    "flags 0x%02x\n", sc->sc_dev.dv_xname, op, addr, cmdlen,
	    len, flags));

	/* Wait for bus to be idle */
	for (retries = 100; retries > 0; retries--) {
		st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, ICH_SMB_HS);
		if (!(st & ICH_SMB_HS_BUSY))
			break;
		DELAY(ICHIIC_DELAY);
	}
	DPRINTF(("%s: exec: st 0x%b\n", sc->sc_dev.dv_xname, st,
	    ICH_SMB_HS_BITS));
	if (st & ICH_SMB_HS_BUSY)
		return (1);

	if (cold || sc->sc_poll)
		flags |= I2C_F_POLL;

	if (!I2C_OP_STOP_P(op) || cmdlen > 1 || len > 2)
		return (1);

	/* Setup transfer */
	sc->sc_i2c_xfer.op = op;
	sc->sc_i2c_xfer.buf = buf;
	sc->sc_i2c_xfer.len = len;
	sc->sc_i2c_xfer.flags = flags;
	sc->sc_i2c_xfer.error = 0;

	/* Set slave address and transfer direction */
	bus_space_write_1(sc->sc_iot, sc->sc_ioh, ICH_SMB_TXSLVA,
	    ICH_SMB_TXSLVA_ADDR(addr) |
	    (I2C_OP_READ_P(op) ? ICH_SMB_TXSLVA_READ : 0));

	b = (void *)cmdbuf;
	if (cmdlen > 0)
		/* Set command byte */
		bus_space_write_1(sc->sc_iot, sc->sc_ioh, ICH_SMB_HCMD, b[0]);

	if (I2C_OP_WRITE_P(op)) {
		/* Write data */
		b = buf;
		if (len > 0)
			bus_space_write_1(sc->sc_iot, sc->sc_ioh,
			    ICH_SMB_HD0, b[0]);
		if (len > 1)
			bus_space_write_1(sc->sc_iot, sc->sc_ioh,
			    ICH_SMB_HD1, b[1]);
	}

	/* Set SMBus command */
	if (len == 0)
		ctl = ICH_SMB_HC_CMD_BYTE;
	else if (len == 1)
		ctl = ICH_SMB_HC_CMD_BDATA;
	else if (len == 2)
		ctl = ICH_SMB_HC_CMD_WDATA;

	if ((flags & I2C_F_POLL) == 0)
		ctl |= ICH_SMB_HC_INTREN;

	/* Start transaction */
	ctl |= ICH_SMB_HC_START;
	bus_space_write_1(sc->sc_iot, sc->sc_ioh, ICH_SMB_HC, ctl);

	if (flags & I2C_F_POLL) {
		/* Poll for completion */
		DELAY(ICHIIC_DELAY);
		for (retries = 1000; retries > 0; retries--) {
			st = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
			    ICH_SMB_HS);
			if ((st & ICH_SMB_HS_BUSY) == 0)
				break;
			DELAY(ICHIIC_DELAY);
		}
		if (st & ICH_SMB_HS_BUSY)
			goto timeout;
		ichiic_intr(sc);
	} else {
		/* Wait for interrupt */
		if (tsleep(sc, PRIBIO, "iicexec", ICHIIC_TIMEOUT * hz))
			goto timeout;
	}

	if (sc->sc_i2c_xfer.error)
		return (1);

	return (0);

timeout:
	/*
	 * Transfer timeout. Kill the transaction and clear status bits.
	 */
	bus_space_write_1(sc->sc_iot, sc->sc_ioh, ICH_SMB_HC,
	    ICH_SMB_HC_KILL);
	DELAY(ICHIIC_DELAY);
	st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, ICH_SMB_HS);
	if ((st & ICH_SMB_HS_FAILED) == 0)
		printf("%s: abort failed, status 0x%b\n",
		    sc->sc_dev.dv_xname, st, ICH_SMB_HS_BITS);
	bus_space_write_1(sc->sc_iot, sc->sc_ioh, ICH_SMB_HS, st);
	return (1);
}
예제 #10
0
파일: amdpm.c 프로젝트: SylvestreG/bitrig
int
amdpm_i2c_exec(void *cookie, i2c_op_t op, i2c_addr_t addr,
    const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags)
{
	struct amdpm_softc *sc = cookie;
	u_int8_t *b;
	u_int16_t st, ctl, data;
	int retries;

	DPRINTF("%s: exec: op %d, addr 0x%02x, cmdlen %d, len %d, "
	    "flags 0x%02x\n", sc->sc_dev.dv_xname, op, addr, cmdlen,
	    len, flags);

	/* Wait for bus to be idle */
	for (retries = 100; retries > 0; retries--) {
		st = bus_space_read_2(sc->sc_iot, sc->sc_i2c_ioh, AMDPM_SMBSTAT);
		if (!(st & AMDPM_SMBSTAT_BSY))
			break;
		DELAY(AMDPM_SMBUS_DELAY);
	}
	DPRINTF("%s: exec: st 0x%b\n", sc->sc_dev.dv_xname, st,
	    AMDPM_SMBSTAT_BITS);
	if (st & AMDPM_SMBSTAT_BSY)
		return (1);

	if (cold || sc->sc_poll)
		flags |= I2C_F_POLL;

	if (!I2C_OP_STOP_P(op) || cmdlen > 1 || len > 2)
		return (1);

	/* Setup transfer */
	sc->sc_i2c_xfer.op = op;
	sc->sc_i2c_xfer.buf = buf;
	sc->sc_i2c_xfer.len = len;
	sc->sc_i2c_xfer.flags = flags;
	sc->sc_i2c_xfer.error = 0;

	/* Set slave address and transfer direction */
	bus_space_write_2(sc->sc_iot, sc->sc_i2c_ioh, AMDPM_SMBADDR,
	    AMDPM_SMBADDR_ADDR(addr) |
	    (I2C_OP_READ_P(op) ? AMDPM_SMBADDR_READ : 0));

	b = (void *)cmdbuf;
	if (cmdlen > 0)
		/* Set command byte */
		bus_space_write_1(sc->sc_iot, sc->sc_i2c_ioh, AMDPM_SMBCMD, b[0]);

	if (I2C_OP_WRITE_P(op)) {
		/* Write data */
		data = 0;
		b = buf;
		if (len > 0)
			data = b[0];
		if (len > 1)
			data |= ((u_int16_t)b[1] << 8);
		if (len > 0)
			bus_space_write_2(sc->sc_iot, sc->sc_i2c_ioh,
			    AMDPM_SMBDATA, data);
	}

	/* Set SMBus command */
	if (len == 0)
		ctl = AMDPM_SMBCTL_CMD_BYTE;
	else if (len == 1)
		ctl = AMDPM_SMBCTL_CMD_BDATA;
	else if (len == 2)
		ctl = AMDPM_SMBCTL_CMD_WDATA;
	else
		panic("%s: unexpected len %zd", __func__, len);

	if ((flags & I2C_F_POLL) == 0)
		ctl |= AMDPM_SMBCTL_CYCEN;

	/* Start transaction */
	ctl |= AMDPM_SMBCTL_START;
	bus_space_write_2(sc->sc_iot, sc->sc_i2c_ioh, AMDPM_SMBCTL, ctl);

	if (flags & I2C_F_POLL) {
		/* Poll for completion */
		DELAY(AMDPM_SMBUS_DELAY);
		for (retries = 1000; retries > 0; retries--) {
			st = bus_space_read_2(sc->sc_iot, sc->sc_i2c_ioh,
			    AMDPM_SMBSTAT);
			if ((st & AMDPM_SMBSTAT_HBSY) == 0)
				break;
			DELAY(AMDPM_SMBUS_DELAY);
		}
		if (st & AMDPM_SMBSTAT_HBSY)
			goto timeout;
		amdpm_intr(sc);
	} else {
		/* Wait for interrupt */
		if (tsleep(sc, PRIBIO, "amdpm", AMDPM_SMBUS_TIMEOUT * hz))
			goto timeout;
	}

	if (sc->sc_i2c_xfer.error)
		return (1);

	return (0);

timeout:
	/*
	 * Transfer timeout. Kill the transaction and clear status bits.
	 */
	printf("%s: exec: op %d, addr 0x%02x, cmdlen %zu, len %zu, "
	    "flags 0x%02x: timeout, status 0x%b\n",
	    sc->sc_dev.dv_xname, op, addr, cmdlen, len, flags,
	    st, AMDPM_SMBSTAT_BITS);
	bus_space_write_2(sc->sc_iot, sc->sc_i2c_ioh, AMDPM_SMBCTL,
	    AMDPM_SMBCTL_ABORT);
	DELAY(AMDPM_SMBUS_DELAY);
	st = bus_space_read_2(sc->sc_iot, sc->sc_i2c_ioh, AMDPM_SMBSTAT);
	if ((st & AMDPM_SMBSTAT_ABRT) == 0)
		printf("%s: abort failed, status 0x%b\n",
		    sc->sc_dev.dv_xname, st, AMDPM_SMBSTAT_BITS);
	bus_space_write_2(sc->sc_iot, sc->sc_i2c_ioh, AMDPM_SMBSTAT, st);
	return (1);
}
예제 #11
0
static int
awin_p2wi_exec(void *priv, i2c_op_t op, i2c_addr_t addr,
    const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags)
{
	struct awin_p2wi_softc *sc = priv;
	uint32_t dlen, ctrl;
	uint8_t rta;
	int error;

	KASSERT(mutex_owned(&sc->sc_lock));

	if (cmdlen != 1 || (len != 1 && len != 2 && len != 4))
		return EINVAL;

	if (sc->sc_rsb_p && sc->sc_rsb_last_da != addr) {
		switch (addr) {
		case AWIN_RSB_ADDR_AXP809:
			rta = AWIN_RSB_RTA_AXP809;
			break;
		case AWIN_RSB_ADDR_AXP806:
			rta = AWIN_RSB_RTA_AXP806;
			break;
		case AWIN_RSB_ADDR_AC100:
			rta = AWIN_RSB_RTA_AC100;
			break;
		default:
			return ENXIO;
		}
		error = awin_p2wi_rsb_config(sc, rta, addr, flags);
		if (error) {
			device_printf(sc->sc_dev,
			    "SRTA failed, flags = %x, error = %d\n",
			    flags, error);
			sc->sc_rsb_last_da = 0;
			return error;
		}

		sc->sc_rsb_last_da = addr;
	}

	/* Data byte register */
	P2WI_WRITE(sc, AWIN_A31_P2WI_DADDR0_REG, *(const uint8_t *)cmdbuf);

	if (I2C_OP_WRITE_P(op)) {
		uint8_t *pbuf = buf;
		uint32_t data;
		/* Write data */
		switch (len) {
		case 1:
			data = pbuf[0];
			break;
		case 2:
			data = pbuf[0] | (pbuf[1] << 8);
			break;
		case 4:
			data = pbuf[0] | (pbuf[1] << 8) |
			    (pbuf[2] << 16) | (pbuf[3] << 24);
			break;
		default:
			return EINVAL;
		}
		P2WI_WRITE(sc, AWIN_A31_P2WI_DATA0_REG, data);
	}

	if (sc->sc_rsb_p) {
		uint8_t cmd;
		if (I2C_OP_WRITE_P(op)) {
			switch (len) {
			case 1:	cmd = AWIN_A80_RSB_CMD_IDX_WR8; break;
			case 2: cmd = AWIN_A80_RSB_CMD_IDX_WR16; break;
			case 4: cmd = AWIN_A80_RSB_CMD_IDX_WR32; break;
			default: return EINVAL;
			}
		} else {
			switch (len) {
			case 1:	cmd = AWIN_A80_RSB_CMD_IDX_RD8; break;
			case 2: cmd = AWIN_A80_RSB_CMD_IDX_RD16; break;
			case 4: cmd = AWIN_A80_RSB_CMD_IDX_RD32; break;
			default: return EINVAL;
			}
		}
		P2WI_WRITE(sc, AWIN_A80_RSB_CMD_REG, cmd);
	}

	/* Program data length register; if reading, set read/write bit */
	dlen = __SHIFTIN(len - 1, AWIN_A31_P2WI_DLEN_ACCESS_LENGTH);
	if (I2C_OP_READ_P(op)) {
		dlen |= AWIN_A31_P2WI_DLEN_READ_WRITE_FLAG;
	}
	P2WI_WRITE(sc, AWIN_A31_P2WI_DLEN_REG, dlen);

	/* Make sure the controller is idle */
	ctrl = P2WI_READ(sc, AWIN_A31_P2WI_CTRL_REG);
	if (ctrl & AWIN_A31_P2WI_CTRL_START_TRANS) {
		device_printf(sc->sc_dev, "device is busy\n");
		return EBUSY;
	}

	/* Start the transfer */
	P2WI_WRITE(sc, AWIN_A31_P2WI_CTRL_REG,
	    ctrl | AWIN_A31_P2WI_CTRL_START_TRANS);

	error = awin_p2wi_wait(sc, flags);
	if (error) {
		return error;
	}

	if (I2C_OP_READ_P(op)) {
		uint32_t data = P2WI_READ(sc, AWIN_A31_P2WI_DATA0_REG);
		switch (len) {
		case 4:
			*(uint32_t *)buf = data;
			break;
		case 2:
			*(uint16_t *)buf = data & 0xffff;
			break;
		case 1:
			*(uint8_t *)buf = data & 0xff;
			break;
		default:
			return EINVAL;
		}
	}

	return 0;
}
예제 #12
0
static int
ichsmb_i2c_exec(void *cookie, i2c_op_t op, i2c_addr_t addr,
    const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags)
{
	struct ichsmb_softc *sc = cookie;
	const uint8_t *b;
	uint8_t ctl = 0, st;
	int retries;
	char fbuf[64];

	DPRINTF(("%s: exec: op %d, addr 0x%02x, cmdlen %zu, len %zu, "
	    "flags 0x%02x\n", device_xname(sc->sc_dev), op, addr, cmdlen,
	    len, flags));

	/* Clear status bits */
	bus_space_write_1(sc->sc_iot, sc->sc_ioh, LPCIB_SMB_HS,
	    LPCIB_SMB_HS_INTR | LPCIB_SMB_HS_DEVERR |
	    LPCIB_SMB_HS_BUSERR | LPCIB_SMB_HS_FAILED);
	bus_space_barrier(sc->sc_iot, sc->sc_ioh, LPCIB_SMB_HS, 1,
	    BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);  

	/* Wait for bus to be idle */
	for (retries = 100; retries > 0; retries--) {
		st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LPCIB_SMB_HS);
		if (!(st & LPCIB_SMB_HS_BUSY))
			break;
		DELAY(ICHIIC_DELAY);
	}
#ifdef ICHIIC_DEBUG
	snprintb(fbuf, sizeof(fbuf), LPCIB_SMB_HS_BITS, st);
	printf("%s: exec: st 0x%s\n", device_xname(sc->sc_dev), fbuf);
#endif
	if (st & LPCIB_SMB_HS_BUSY)
		return (1);

	if (cold || sc->sc_poll)
		flags |= I2C_F_POLL;

	if (!I2C_OP_STOP_P(op) || cmdlen > 1 || len > 2 ||
	    (cmdlen == 0 && len > 1))
		return (1);

	/* Setup transfer */
	sc->sc_i2c_xfer.op = op;
	sc->sc_i2c_xfer.buf = buf;
	sc->sc_i2c_xfer.len = len;
	sc->sc_i2c_xfer.flags = flags;
	sc->sc_i2c_xfer.error = 0;

	/* Set slave address and transfer direction */
	bus_space_write_1(sc->sc_iot, sc->sc_ioh, LPCIB_SMB_TXSLVA,
	    LPCIB_SMB_TXSLVA_ADDR(addr) |
	    (I2C_OP_READ_P(op) ? LPCIB_SMB_TXSLVA_READ : 0));

	b = (const uint8_t *)cmdbuf;
	if (cmdlen > 0)
		/* Set command byte */
		bus_space_write_1(sc->sc_iot, sc->sc_ioh, LPCIB_SMB_HCMD, b[0]);

	if (I2C_OP_WRITE_P(op)) {
		/* Write data */
		b = buf;
		if (cmdlen == 0 && len == 1)
			bus_space_write_1(sc->sc_iot, sc->sc_ioh,
			    LPCIB_SMB_HCMD, b[0]);
		else if (len > 0)
			bus_space_write_1(sc->sc_iot, sc->sc_ioh,
			    LPCIB_SMB_HD0, b[0]);
		if (len > 1)
			bus_space_write_1(sc->sc_iot, sc->sc_ioh,
			    LPCIB_SMB_HD1, b[1]);
	}

	/* Set SMBus command */
	if (cmdlen == 0) {
		if (len == 0)
			ctl = LPCIB_SMB_HC_CMD_QUICK;
		else
			ctl = LPCIB_SMB_HC_CMD_BYTE;
	} else if (len == 1)
		ctl = LPCIB_SMB_HC_CMD_BDATA;
	else if (len == 2)
		ctl = LPCIB_SMB_HC_CMD_WDATA;

	if ((flags & I2C_F_POLL) == 0)
		ctl |= LPCIB_SMB_HC_INTREN;

	/* Start transaction */
	ctl |= LPCIB_SMB_HC_START;
	bus_space_write_1(sc->sc_iot, sc->sc_ioh, LPCIB_SMB_HC, ctl);

	if (flags & I2C_F_POLL) {
		/* Poll for completion */
		DELAY(ICHIIC_DELAY);
		for (retries = 1000; retries > 0; retries--) {
			st = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
			    LPCIB_SMB_HS);
			if ((st & LPCIB_SMB_HS_BUSY) == 0)
				break;
			DELAY(ICHIIC_DELAY);
		}
		if (st & LPCIB_SMB_HS_BUSY)
			goto timeout;
		ichsmb_intr(sc);
	} else {
		/* Wait for interrupt */
		if (tsleep(sc, PRIBIO, "iicexec", ICHIIC_TIMEOUT * hz))
			goto timeout;
	}

	if (sc->sc_i2c_xfer.error)
		return (1);

	return (0);

timeout:
	/*
	 * Transfer timeout. Kill the transaction and clear status bits.
	 */
	snprintb(fbuf, sizeof(fbuf), LPCIB_SMB_HS_BITS, st);
	aprint_error_dev(sc->sc_dev,
	    "exec: op %d, addr 0x%02x, cmdlen %zd, len %zd, "
	    "flags 0x%02x: timeout, status 0x%s\n",
	    op, addr, cmdlen, len, flags, fbuf);
	bus_space_write_1(sc->sc_iot, sc->sc_ioh, LPCIB_SMB_HC,
	    LPCIB_SMB_HC_KILL);
	DELAY(ICHIIC_DELAY);
	st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LPCIB_SMB_HS);
	if ((st & LPCIB_SMB_HS_FAILED) == 0) {
		snprintb(fbuf, sizeof(fbuf), LPCIB_SMB_HS_BITS, st);
		aprint_error_dev(sc->sc_dev, "abort failed, status 0x%s\n",
		    fbuf);
	}
	bus_space_write_1(sc->sc_iot, sc->sc_ioh, LPCIB_SMB_HS, st);
	return (1);
}
예제 #13
0
static int
piixpm_i2c_exec(void *cookie, i2c_op_t op, i2c_addr_t addr,
    const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags)
{
	struct piixpm_smbus *smbus = cookie;
	struct piixpm_softc *sc = smbus->softc;
	const u_int8_t *b;
	u_int8_t ctl = 0, st;
	int retries;

	DPRINTF(("%s: exec: op %d, addr 0x%x, cmdlen %zu, len %zu, flags 0x%x\n",
	    device_xname(sc->sc_dev), op, addr, cmdlen, len, flags));

	/* Clear status bits */
	bus_space_write_1(sc->sc_smb_iot, sc->sc_smb_ioh, PIIX_SMB_HS, 
	    PIIX_SMB_HS_INTR | PIIX_SMB_HS_DEVERR | 
	    PIIX_SMB_HS_BUSERR | PIIX_SMB_HS_FAILED);
	bus_space_barrier(sc->sc_smb_iot, sc->sc_smb_ioh, PIIX_SMB_HS, 1,
	    BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);

	/* Wait for bus to be idle */
	for (retries = 100; retries > 0; retries--) {
		st = bus_space_read_1(sc->sc_smb_iot, sc->sc_smb_ioh,
		    PIIX_SMB_HS);
		if (!(st & PIIX_SMB_HS_BUSY))
			break;
		DELAY(PIIXPM_DELAY);
	}
	DPRINTF(("%s: exec: st 0x%d\n", device_xname(sc->sc_dev), st & 0xff));
	if (st & PIIX_SMB_HS_BUSY)
		return (1);

	if (cold || sc->sc_poll)
		flags |= I2C_F_POLL;

	if (!I2C_OP_STOP_P(op) || cmdlen > 1 || len > 2 ||
	    (cmdlen == 0 && len > 1))
		return (1);

	/* Setup transfer */
	sc->sc_i2c_xfer.op = op;
	sc->sc_i2c_xfer.buf = buf;
	sc->sc_i2c_xfer.len = len;
	sc->sc_i2c_xfer.flags = flags;
	sc->sc_i2c_xfer.error = 0;

	/* Set slave address and transfer direction */
	bus_space_write_1(sc->sc_smb_iot, sc->sc_smb_ioh, PIIX_SMB_TXSLVA,
	    PIIX_SMB_TXSLVA_ADDR(addr) |
	    (I2C_OP_READ_P(op) ? PIIX_SMB_TXSLVA_READ : 0));

	b = cmdbuf;
	if (cmdlen > 0)
		/* Set command byte */
		bus_space_write_1(sc->sc_smb_iot, sc->sc_smb_ioh,
		    PIIX_SMB_HCMD, b[0]);

	if (I2C_OP_WRITE_P(op)) {
		/* Write data */
		b = buf;
		if (cmdlen == 0 && len == 1)
			bus_space_write_1(sc->sc_smb_iot, sc->sc_smb_ioh,
			    PIIX_SMB_HCMD, b[0]);
		else if (len > 0)
			bus_space_write_1(sc->sc_smb_iot, sc->sc_smb_ioh,
			    PIIX_SMB_HD0, b[0]);
		if (len > 1)
			bus_space_write_1(sc->sc_smb_iot, sc->sc_smb_ioh,
			    PIIX_SMB_HD1, b[1]);
	}

	/* Set SMBus command */
	if (cmdlen == 0) {
		if (len == 0)
			ctl = PIIX_SMB_HC_CMD_QUICK;
		else
			ctl = PIIX_SMB_HC_CMD_BYTE;
	} else if (len == 1)
		ctl = PIIX_SMB_HC_CMD_BDATA;
	else if (len == 2)
		ctl = PIIX_SMB_HC_CMD_WDATA;

	if ((flags & I2C_F_POLL) == 0)
		ctl |= PIIX_SMB_HC_INTREN;

	/* Start transaction */
	ctl |= PIIX_SMB_HC_START;
	bus_space_write_1(sc->sc_smb_iot, sc->sc_smb_ioh, PIIX_SMB_HC, ctl);

	if (flags & I2C_F_POLL) {
		/* Poll for completion */
		if (PIIXPM_IS_CSB5(sc->sc_id))
			DELAY(2*PIIXPM_DELAY);
		else
			DELAY(PIIXPM_DELAY);
		for (retries = 1000; retries > 0; retries--) {
			st = bus_space_read_1(sc->sc_smb_iot, sc->sc_smb_ioh,
			    PIIX_SMB_HS);
			if ((st & PIIX_SMB_HS_BUSY) == 0)
				break;
			DELAY(PIIXPM_DELAY);
		}
		if (st & PIIX_SMB_HS_BUSY)
			goto timeout;
		piixpm_intr(smbus);
	} else {
		/* Wait for interrupt */
		if (tsleep(sc, PRIBIO, "iicexec", PIIXPM_TIMEOUT * hz))
			goto timeout;
	}

	if (sc->sc_i2c_xfer.error)
		return (1);

	return (0);

timeout:
	/*
	 * Transfer timeout. Kill the transaction and clear status bits.
	 */
	aprint_error_dev(sc->sc_dev, "timeout, status 0x%x\n", st);
	bus_space_write_1(sc->sc_smb_iot, sc->sc_smb_ioh, PIIX_SMB_HC,
	    PIIX_SMB_HC_KILL);
	DELAY(PIIXPM_DELAY);
	st = bus_space_read_1(sc->sc_smb_iot, sc->sc_smb_ioh, PIIX_SMB_HS);
	if ((st & PIIX_SMB_HS_FAILED) == 0)
		aprint_error_dev(sc->sc_dev, "transaction abort failed, status 0x%x\n", st);
	bus_space_write_1(sc->sc_smb_iot, sc->sc_smb_ioh, PIIX_SMB_HS, st);
	/*
	 * CSB5 needs hard reset to unlock the smbus after timeout.
	 */
	if (PIIXPM_IS_CSB5(sc->sc_id))
		piixpm_csb5_reset(sc);
	return (1);
}
예제 #14
0
파일: nviic.c 프로젝트: SylvestreG/bitrig
int
nviic_i2c_exec(void *arg, i2c_op_t op, i2c_addr_t addr,
    const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags)
{
	struct nviic_controller		*nc = arg;
#ifdef NVIIC_DEBUG
	struct nviic_softc		*sc = nc->nc_sc;
#endif
	u_int8_t			protocol;
	u_int8_t			*b;
	u_int8_t			sts;
	int				i;

	DPRINTF("%s: exec op: %d addr: 0x%x cmdlen: %d len: %d flags 0x%x\n",
	    DEVNAME(sc), op, addr, cmdlen, len, flags);

	if (cold)
		flags |= I2C_F_POLL;

	if (I2C_OP_STOP_P(op) == 0 || cmdlen > 1 || len > 2)
		return (1);

	/* set slave address */
	nviic_write(nc, NVI_SMB_ADDR, addr << 1);

	/* set command byte */
	if (cmdlen > 0) {
		b = (u_int8_t *)cmdbuf;
		nviic_write(nc, NVI_SMB_CMD, b[0]);
	}

	b = (u_int8_t *)buf;

	/* write data */
	if (I2C_OP_WRITE_P(op)) {
		for (i = 0; i < len; i++)
			nviic_write(nc, NVI_SMB_DATA(i), b[i]);
	}

	switch (len) {
	case 0:
		protocol = NVI_SMB_PRTCL_BYTE;
		break;
	case 1:
		protocol = NVI_SMB_PRTCL_BYTE_DATA;
		break;
	case 2:
		protocol = NVI_SMB_PRTCL_WORD_DATA;
		break;
	}

	/* set direction */
	if (I2C_OP_READ_P(op))
		protocol |= NVI_SMB_PRTCL_READ;

	/* start transaction */
	nviic_write(nc, NVI_SMB_PRTCL, protocol);

	for (i = 1000; i > 0; i--) {
		delay(100);
		if (nviic_read(nc, NVI_SMB_PRTCL) == 0)
			break;
	}
	if (i == 0) {
		DPRINTF("%s: timeout\n", DEVNAME(sc));
		return (1);
	}

	sts = nviic_read(nc, NVI_SMB_STS);
	if (sts & NVI_SMB_STS_STATUS)
		return (1);

	/* read data */
	if (I2C_OP_READ_P(op)) {
		for (i = 0; i < len; i++)
			b[i] = nviic_read(nc, NVI_SMB_DATA(i));
	}

	return (0);
}
예제 #15
0
int
amdiic_i2c_exec(void *cookie, i2c_op_t op, i2c_addr_t addr,
    const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags)
{
	struct amdiic_softc *sc = cookie;
	u_int8_t *b;
	u_int8_t proto, st;
	int retries;

	DPRINTF(("%s: exec: op %d, addr 0x%02x, cmdlen %d, len %d, "
	    "flags 0x%02x\n", sc->sc_dev.dv_xname, op, addr,
	    cmdlen, len, flags));

	if (cold || sc->sc_poll)
		flags |= I2C_F_POLL;

	if (!I2C_OP_STOP_P(op) || cmdlen > 1 || len > 2)
		return (1);

	/* Setup transfer */
	sc->sc_i2c_xfer.op = op;
	sc->sc_i2c_xfer.buf = buf;
	sc->sc_i2c_xfer.len = len;
	sc->sc_i2c_xfer.flags = flags;
	sc->sc_i2c_xfer.error = 0;

	/* Set slave address */
	if (amdiic_write(sc, AMD8111_SMB_ADDR,
	    addr << AMD8111_SMB_ADDR_SHIFT) == -1)
		return (1);

	b = (void *)cmdbuf;
	if (cmdlen > 0)
		/* Set command byte */
		if (amdiic_write(sc, AMD8111_SMB_CMD, b[0]) == -1)
			return (1);

	if (I2C_OP_WRITE_P(op)) {
		/* Write data */
		b = buf;
		if (len > 0)
			if (amdiic_write(sc, AMD8111_SMB_DATA(0), b[0]) == -1)
				return (1);
		if (len > 1)
			if (amdiic_write(sc, AMD8111_SMB_DATA(1), b[1]) == -1)
				return (1);
	}

	/* Set SMBus command */
	if (len == 0)
		proto = AMD8111_SMB_PROTO_BYTE;
	else if (len == 1)
		proto = AMD8111_SMB_PROTO_BDATA;
	else if (len == 2)
		proto = AMD8111_SMB_PROTO_WDATA;

	/* Set direction */
	if (I2C_OP_READ_P(op))
		proto |= AMD8111_SMB_PROTO_READ;

	/* Start transaction */
	amdiic_write(sc, AMD8111_SMB_PROTO, proto);

	if (flags & I2C_F_POLL) {
		/* Poll for completion */
		DELAY(AMDIIC_DELAY);
		for (retries = 1000; retries > 0; retries--) {
			st = amdiic_read(sc, AMD8111_SMB_STAT);
			if (st != 0)
				break;
			DELAY(AMDIIC_DELAY);
		}
		if (st == 0) {
			printf("%s: exec: op %d, addr 0x%02x, cmdlen %d, "
			    "len %d, flags 0x%02x: timeout\n",
			    sc->sc_dev.dv_xname, op, addr, cmdlen, len, flags);
			return (1);
		}
		amdiic_intr(sc);
	} else {
		/* Wait for interrupt */
		if (tsleep(sc, PRIBIO, "amdiic", AMDIIC_TIMEOUT * hz))
			return (1);
	}	

	if (sc->sc_i2c_xfer.error)
		return (1);

	return (0);
}
예제 #16
0
파일: sociic.c 프로젝트: ajinkya93/OpenBSD
int
sociic_i2c_exec(void *arg, i2c_op_t op, i2c_addr_t addr,
    const void *vcmdbuf, size_t cmdlen, void *vbuf, size_t buflen, int flags)
{
	struct sociic_softc *sc = arg;
	const uint8_t *cmdbuf = vcmdbuf;
	uint8_t *buf = vbuf;
	int err = 0;
	size_t len;
	uint8_t val;

	/* Clear the bus. */
	sociic_write(sc, I2C_SR, 0);
	sociic_write(sc, I2C_CR, I2C_CR_MEN);
	err = sociic_wait_bus(sc);
	if (err)
		return (err);

	if (cmdlen > 0) {
		sociic_write(sc, I2C_CR, I2C_CR_MEN|I2C_CR_MSTA|I2C_CR_MTX);
		sociic_write(sc, I2C_DR, addr << 1);
		err = sociic_wait(sc, I2C_F_WRITE);
		if (err)
			goto out;

		len = cmdlen;
		while (len--) {
			sociic_write(sc, I2C_DR, *cmdbuf++);
			err = sociic_wait(sc, I2C_F_WRITE);
			if (err)
				goto out;
		}
	}

	if (I2C_OP_READ_P(op) && buflen > 0) {
		/* RESTART if we did write a command above. */
		val = I2C_CR_MEN|I2C_CR_MSTA|I2C_CR_MTX;
		if (cmdlen > 0)
			val |= I2C_CR_RSTA;
		sociic_write(sc, I2C_CR, val);
		sociic_write(sc, I2C_DR, (addr << 1) | 1);
		err = sociic_wait(sc, I2C_F_WRITE);
		if (err)
			goto out;

		/* NACK if we're only sending one byte. */
		val = I2C_CR_MEN|I2C_CR_MSTA;
		if (buflen == 1)
			val |= I2C_CR_TXAK;
		sociic_write(sc, I2C_CR, val);

		/* Dummy read. */
		sociic_read(sc, I2C_DR);

		len = buflen;
		while (len--) {
			err = sociic_wait(sc, I2C_F_READ);
			if (err)
				goto out;

			/* NACK on last byte. */
			if (len == 1)
				sociic_write(sc, I2C_CR,
				    I2C_CR_MEN|I2C_CR_MSTA|I2C_CR_TXAK);
			/* STOP after last byte. */
			if (len == 0)
				sociic_write(sc, I2C_CR,
				    I2C_CR_MEN|I2C_CR_TXAK);
			*buf++ = sociic_read(sc, I2C_DR);
		}
	}

	if (I2C_OP_WRITE_P(op) && cmdlen == 0 && buflen > 0) {
		/* START if we didn't write a command. */
		sociic_write(sc, I2C_CR, I2C_CR_MEN|I2C_CR_MSTA|I2C_CR_MTX);
		sociic_write(sc, I2C_DR, addr << 1);
		err = sociic_wait(sc, I2C_F_WRITE);
		if (err)
			goto out;
	}

	if (I2C_OP_WRITE_P(op) && buflen > 0) {
		len = buflen;
		while (len--) {
			sociic_write(sc, I2C_DR, *buf++);
			err = sociic_wait(sc, I2C_F_WRITE);
			if (err)
				goto out;
		}
	}

out:
	/* STOP if we're still holding the bus. */
	sociic_write(sc, I2C_CR, I2C_CR_MEN);
	return (err);
}