14e190a62SNicolas Souchu /*-
2*4d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
3718cf2ccSPedro F. Giffuni *
44e190a62SNicolas Souchu * Copyright (c) 1998 Nicolas Souchu, Marc Bouget
5e0efc557SJoerg Wunsch * Copyright (c) 2004 Joerg Wunsch
64e190a62SNicolas Souchu * All rights reserved.
74e190a62SNicolas Souchu *
84e190a62SNicolas Souchu * Redistribution and use in source and binary forms, with or without
94e190a62SNicolas Souchu * modification, are permitted provided that the following conditions
104e190a62SNicolas Souchu * are met:
114e190a62SNicolas Souchu * 1. Redistributions of source code must retain the above copyright
124e190a62SNicolas Souchu * notice, this list of conditions and the following disclaimer.
134e190a62SNicolas Souchu * 2. Redistributions in binary form must reproduce the above copyright
144e190a62SNicolas Souchu * notice, this list of conditions and the following disclaimer in the
154e190a62SNicolas Souchu * documentation and/or other materials provided with the distribution.
164e190a62SNicolas Souchu *
174e190a62SNicolas Souchu * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
184e190a62SNicolas Souchu * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
194e190a62SNicolas Souchu * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
204e190a62SNicolas Souchu * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
214e190a62SNicolas Souchu * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
224e190a62SNicolas Souchu * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
234e190a62SNicolas Souchu * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
244e190a62SNicolas Souchu * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
254e190a62SNicolas Souchu * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
264e190a62SNicolas Souchu * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
274e190a62SNicolas Souchu * SUCH DAMAGE.
284e190a62SNicolas Souchu */
29006124d8SDavid E. O'Brien
304e190a62SNicolas Souchu #include <sys/param.h>
3113e3657bSJohn Baldwin #include <sys/bus.h>
3213e3657bSJohn Baldwin #include <sys/lock.h>
334e190a62SNicolas Souchu #include <sys/kernel.h>
344e190a62SNicolas Souchu #include <sys/module.h>
3513e3657bSJohn Baldwin #include <sys/mutex.h>
3613e3657bSJohn Baldwin #include <sys/systm.h>
374e190a62SNicolas Souchu
380f210c92SNicolas Souchu #include <machine/bus.h>
390f210c92SNicolas Souchu #include <machine/resource.h>
40e0efc557SJoerg Wunsch
410f210c92SNicolas Souchu #include <sys/rman.h>
420f210c92SNicolas Souchu
439e58d59fSJohn Baldwin #include <dev/iicbus/iicbus.h>
444e190a62SNicolas Souchu #include <dev/iicbus/iiconf.h>
45e0efc557SJoerg Wunsch #include <dev/pcf/pcfvar.h>
464e190a62SNicolas Souchu #include "iicbus_if.h"
474e190a62SNicolas Souchu
48e0efc557SJoerg Wunsch /* Not so official debugging option. */
49e0efc557SJoerg Wunsch /* #define PCFDEBUG */
500f210c92SNicolas Souchu
51e0efc557SJoerg Wunsch static int pcf_wait_byte(struct pcf_softc *pcf);
52e0efc557SJoerg Wunsch static int pcf_noack(struct pcf_softc *pcf, int timeout);
5313e3657bSJohn Baldwin static void pcf_stop_locked(struct pcf_softc *pcf);
544e190a62SNicolas Souchu
554e190a62SNicolas Souchu /*
564e190a62SNicolas Souchu * Polling mode for master operations wait for a new
5713e3657bSJohn Baldwin * byte incoming or outgoing
584e190a62SNicolas Souchu */
5913e3657bSJohn Baldwin static int
pcf_wait_byte(struct pcf_softc * sc)60e0efc557SJoerg Wunsch pcf_wait_byte(struct pcf_softc *sc)
614e190a62SNicolas Souchu {
624e190a62SNicolas Souchu int counter = TIMEOUT;
634e190a62SNicolas Souchu
6413e3657bSJohn Baldwin PCF_ASSERT_LOCKED(sc);
654e190a62SNicolas Souchu while (counter--) {
66e0efc557SJoerg Wunsch if ((pcf_get_S1(sc) & PIN) == 0)
674e190a62SNicolas Souchu return (0);
684e190a62SNicolas Souchu }
694e190a62SNicolas Souchu
704f16b8b1SNicolas Souchu #ifdef PCFDEBUG
714f16b8b1SNicolas Souchu printf("pcf: timeout!\n");
724f16b8b1SNicolas Souchu #endif
734f16b8b1SNicolas Souchu
744e190a62SNicolas Souchu return (IIC_ETIMEOUT);
754e190a62SNicolas Souchu }
764e190a62SNicolas Souchu
7713e3657bSJohn Baldwin static void
pcf_stop_locked(struct pcf_softc * sc)7813e3657bSJohn Baldwin pcf_stop_locked(struct pcf_softc *sc)
794e190a62SNicolas Souchu {
804e190a62SNicolas Souchu
8113e3657bSJohn Baldwin PCF_ASSERT_LOCKED(sc);
82e0efc557SJoerg Wunsch #ifdef PCFDEBUG
83e0efc557SJoerg Wunsch device_printf(dev, " >> stop\n");
84e0efc557SJoerg Wunsch #endif
85af548787SNicolas Souchu /*
86af548787SNicolas Souchu * Send STOP condition iff the START condition was previously sent.
879d5abbddSJens Schweikhardt * STOP is sent only once even if an iicbus_stop() is called after
88e0efc557SJoerg Wunsch * an iicbus_read()... see pcf_read(): the PCF needs to send the stop
89af548787SNicolas Souchu * before the last char is read.
90af548787SNicolas Souchu */
91e0efc557SJoerg Wunsch if (sc->pcf_started) {
924e190a62SNicolas Souchu /* set stop condition and enable IT */
93e0efc557SJoerg Wunsch pcf_set_S1(sc, PIN|ESO|ENI|STO|ACK);
944e190a62SNicolas Souchu
95e0efc557SJoerg Wunsch sc->pcf_started = 0;
96af548787SNicolas Souchu }
974e190a62SNicolas Souchu }
984e190a62SNicolas Souchu
9913e3657bSJohn Baldwin static int
pcf_noack(struct pcf_softc * sc,int timeout)100e0efc557SJoerg Wunsch pcf_noack(struct pcf_softc *sc, int timeout)
101af548787SNicolas Souchu {
102af548787SNicolas Souchu int noack;
103af548787SNicolas Souchu int k = timeout/10;
104af548787SNicolas Souchu
10513e3657bSJohn Baldwin PCF_ASSERT_LOCKED(sc);
106af548787SNicolas Souchu do {
107e0efc557SJoerg Wunsch noack = pcf_get_S1(sc) & LRB;
108af548787SNicolas Souchu if (!noack)
109af548787SNicolas Souchu break;
110af548787SNicolas Souchu DELAY(10); /* XXX wait 10 us */
111af548787SNicolas Souchu } while (k--);
112af548787SNicolas Souchu
113af548787SNicolas Souchu return (noack);
114af548787SNicolas Souchu }
115af548787SNicolas Souchu
116e0efc557SJoerg Wunsch int
pcf_repeated_start(device_t dev,u_char slave,int timeout)117e0efc557SJoerg Wunsch pcf_repeated_start(device_t dev, u_char slave, int timeout)
1184e190a62SNicolas Souchu {
119e0efc557SJoerg Wunsch struct pcf_softc *sc = DEVTOSOFTC(dev);
1204e190a62SNicolas Souchu int error = 0;
1214e190a62SNicolas Souchu
12213e3657bSJohn Baldwin PCF_LOCK(sc);
123e0efc557SJoerg Wunsch #ifdef PCFDEBUG
124e0efc557SJoerg Wunsch device_printf(dev, " >> repeated start for slave %#x\n",
125e0efc557SJoerg Wunsch (unsigned)slave);
126e0efc557SJoerg Wunsch #endif
1274e190a62SNicolas Souchu /* repeated start */
128e0efc557SJoerg Wunsch pcf_set_S1(sc, ESO|STA|STO|ACK);
1294e190a62SNicolas Souchu
1304e190a62SNicolas Souchu /* set slave address to PCF. Last bit (LSB) must be set correctly
1314e190a62SNicolas Souchu * according to transfer direction */
132e0efc557SJoerg Wunsch pcf_set_S0(sc, slave);
1334e190a62SNicolas Souchu
1344e190a62SNicolas Souchu /* wait for address sent, polling */
135e0efc557SJoerg Wunsch if ((error = pcf_wait_byte(sc)))
1364e190a62SNicolas Souchu goto error;
1374e190a62SNicolas Souchu
138af548787SNicolas Souchu /* check for ack */
139e0efc557SJoerg Wunsch if (pcf_noack(sc, timeout)) {
1404e190a62SNicolas Souchu error = IIC_ENOACK;
1414f16b8b1SNicolas Souchu #ifdef PCFDEBUG
1424f16b8b1SNicolas Souchu printf("pcf: no ack on repeated_start!\n");
1434f16b8b1SNicolas Souchu #endif
1444e190a62SNicolas Souchu goto error;
1454e190a62SNicolas Souchu }
1464e190a62SNicolas Souchu
14713e3657bSJohn Baldwin PCF_UNLOCK(sc);
1484e190a62SNicolas Souchu return (0);
1494e190a62SNicolas Souchu
1504e190a62SNicolas Souchu error:
15113e3657bSJohn Baldwin pcf_stop_locked(sc);
15213e3657bSJohn Baldwin PCF_UNLOCK(sc);
1534e190a62SNicolas Souchu return (error);
1544e190a62SNicolas Souchu }
1554e190a62SNicolas Souchu
156e0efc557SJoerg Wunsch int
pcf_start(device_t dev,u_char slave,int timeout)157e0efc557SJoerg Wunsch pcf_start(device_t dev, u_char slave, int timeout)
1584e190a62SNicolas Souchu {
159e0efc557SJoerg Wunsch struct pcf_softc *sc = DEVTOSOFTC(dev);
1604e190a62SNicolas Souchu int error = 0;
1614e190a62SNicolas Souchu
16213e3657bSJohn Baldwin PCF_LOCK(sc);
163e0efc557SJoerg Wunsch #ifdef PCFDEBUG
164e0efc557SJoerg Wunsch device_printf(dev, " >> start for slave %#x\n", (unsigned)slave);
165e0efc557SJoerg Wunsch #endif
1664f16b8b1SNicolas Souchu if ((pcf_get_S1(sc) & nBB) == 0) {
1674f16b8b1SNicolas Souchu #ifdef PCFDEBUG
1684f16b8b1SNicolas Souchu printf("pcf: busy!\n");
1694f16b8b1SNicolas Souchu #endif
17013e3657bSJohn Baldwin PCF_UNLOCK(sc);
171d1e99670SIan Lepore return (IIC_EBUSERR);
1724f16b8b1SNicolas Souchu }
1734e190a62SNicolas Souchu
1744e190a62SNicolas Souchu /* set slave address to PCF. Last bit (LSB) must be set correctly
1754e190a62SNicolas Souchu * according to transfer direction */
176e0efc557SJoerg Wunsch pcf_set_S0(sc, slave);
1774e190a62SNicolas Souchu
1784e190a62SNicolas Souchu /* START only */
179e0efc557SJoerg Wunsch pcf_set_S1(sc, PIN|ESO|STA|ACK);
1804e190a62SNicolas Souchu
181e0efc557SJoerg Wunsch sc->pcf_started = 1;
182af548787SNicolas Souchu
1834e190a62SNicolas Souchu /* wait for address sent, polling */
184e0efc557SJoerg Wunsch if ((error = pcf_wait_byte(sc)))
1854e190a62SNicolas Souchu goto error;
1864e190a62SNicolas Souchu
187af548787SNicolas Souchu /* check for ACK */
188e0efc557SJoerg Wunsch if (pcf_noack(sc, timeout)) {
1894e190a62SNicolas Souchu error = IIC_ENOACK;
1904f16b8b1SNicolas Souchu #ifdef PCFDEBUG
1914f16b8b1SNicolas Souchu printf("pcf: no ack on start!\n");
1924f16b8b1SNicolas Souchu #endif
1934e190a62SNicolas Souchu goto error;
1944e190a62SNicolas Souchu }
1954e190a62SNicolas Souchu
19613e3657bSJohn Baldwin PCF_UNLOCK(sc);
1974e190a62SNicolas Souchu return (0);
1984e190a62SNicolas Souchu
1994e190a62SNicolas Souchu error:
20013e3657bSJohn Baldwin pcf_stop_locked(sc);
20113e3657bSJohn Baldwin PCF_UNLOCK(sc);
2024e190a62SNicolas Souchu return (error);
2034e190a62SNicolas Souchu }
2044e190a62SNicolas Souchu
20513e3657bSJohn Baldwin int
pcf_stop(device_t dev)20613e3657bSJohn Baldwin pcf_stop(device_t dev)
20713e3657bSJohn Baldwin {
20813e3657bSJohn Baldwin struct pcf_softc *sc = DEVTOSOFTC(dev);
20913e3657bSJohn Baldwin
21013e3657bSJohn Baldwin #ifdef PCFDEBUG
21113e3657bSJohn Baldwin device_printf(dev, " >> stop\n");
21213e3657bSJohn Baldwin #endif
21313e3657bSJohn Baldwin PCF_LOCK(sc);
21413e3657bSJohn Baldwin pcf_stop_locked(sc);
21513e3657bSJohn Baldwin PCF_UNLOCK(sc);
21613e3657bSJohn Baldwin
21713e3657bSJohn Baldwin return (0);
21813e3657bSJohn Baldwin }
21913e3657bSJohn Baldwin
220e0efc557SJoerg Wunsch void
pcf_intr(void * arg)221e0efc557SJoerg Wunsch pcf_intr(void *arg)
2224e190a62SNicolas Souchu {
2234f16b8b1SNicolas Souchu struct pcf_softc *sc = arg;
2244e190a62SNicolas Souchu char data, status, addr;
2254e190a62SNicolas Souchu char error = 0;
2264e190a62SNicolas Souchu
22713e3657bSJohn Baldwin PCF_LOCK(sc);
228e0efc557SJoerg Wunsch status = pcf_get_S1(sc);
2294e190a62SNicolas Souchu
2304e190a62SNicolas Souchu if (status & PIN) {
2314f16b8b1SNicolas Souchu printf("pcf: spurious interrupt, status=0x%x\n",
232e0efc557SJoerg Wunsch status & 0xff);
2334e190a62SNicolas Souchu
2344e190a62SNicolas Souchu goto error;
2354e190a62SNicolas Souchu }
2364e190a62SNicolas Souchu
2374e190a62SNicolas Souchu if (status & LAB)
2384f16b8b1SNicolas Souchu printf("pcf: bus arbitration lost!\n");
2394e190a62SNicolas Souchu
2404e190a62SNicolas Souchu if (status & BER) {
2414e190a62SNicolas Souchu error = IIC_EBUSERR;
242e0efc557SJoerg Wunsch iicbus_intr(sc->iicbus, INTR_ERROR, &error);
2434e190a62SNicolas Souchu
2444e190a62SNicolas Souchu goto error;
2454e190a62SNicolas Souchu }
2464e190a62SNicolas Souchu
2474e190a62SNicolas Souchu do {
248e0efc557SJoerg Wunsch status = pcf_get_S1(sc);
2494e190a62SNicolas Souchu
250e0efc557SJoerg Wunsch switch(sc->pcf_slave_mode) {
2514e190a62SNicolas Souchu case SLAVE_TRANSMITTER:
2524e190a62SNicolas Souchu if (status & LRB) {
2534e190a62SNicolas Souchu /* ack interrupt line */
254e0efc557SJoerg Wunsch dummy_write(sc);
2554e190a62SNicolas Souchu
2564e190a62SNicolas Souchu /* no ack, don't send anymore */
257e0efc557SJoerg Wunsch sc->pcf_slave_mode = SLAVE_RECEIVER;
2584e190a62SNicolas Souchu
259e0efc557SJoerg Wunsch iicbus_intr(sc->iicbus, INTR_NOACK, NULL);
2604e190a62SNicolas Souchu break;
2614e190a62SNicolas Souchu }
2624e190a62SNicolas Souchu
2634e190a62SNicolas Souchu /* get data from upper code */
264e0efc557SJoerg Wunsch iicbus_intr(sc->iicbus, INTR_TRANSMIT, &data);
2654e190a62SNicolas Souchu
266e0efc557SJoerg Wunsch pcf_set_S0(sc, data);
2674e190a62SNicolas Souchu break;
2684e190a62SNicolas Souchu
2694e190a62SNicolas Souchu case SLAVE_RECEIVER:
2704e190a62SNicolas Souchu if (status & AAS) {
271e0efc557SJoerg Wunsch addr = pcf_get_S0(sc);
2724e190a62SNicolas Souchu
2734e190a62SNicolas Souchu if (status & AD0)
274e0efc557SJoerg Wunsch iicbus_intr(sc->iicbus, INTR_GENERAL, &addr);
2754e190a62SNicolas Souchu else
276e0efc557SJoerg Wunsch iicbus_intr(sc->iicbus, INTR_START, &addr);
2774e190a62SNicolas Souchu
2784e190a62SNicolas Souchu if (addr & LSB) {
279e0efc557SJoerg Wunsch sc->pcf_slave_mode = SLAVE_TRANSMITTER;
2804e190a62SNicolas Souchu
2814e190a62SNicolas Souchu /* get the first char from upper code */
282e0efc557SJoerg Wunsch iicbus_intr(sc->iicbus, INTR_TRANSMIT, &data);
2834e190a62SNicolas Souchu
2844e190a62SNicolas Souchu /* send first data byte */
285e0efc557SJoerg Wunsch pcf_set_S0(sc, data);
2864e190a62SNicolas Souchu }
2874e190a62SNicolas Souchu
2884e190a62SNicolas Souchu break;
2894e190a62SNicolas Souchu }
2904e190a62SNicolas Souchu
2914e190a62SNicolas Souchu /* stop condition received? */
2924e190a62SNicolas Souchu if (status & STS) {
2934e190a62SNicolas Souchu /* ack interrupt line */
294e0efc557SJoerg Wunsch dummy_read(sc);
2954e190a62SNicolas Souchu
2964e190a62SNicolas Souchu /* emulate intr stop condition */
297e0efc557SJoerg Wunsch iicbus_intr(sc->iicbus, INTR_STOP, NULL);
2984e190a62SNicolas Souchu
2994e190a62SNicolas Souchu } else {
3004e190a62SNicolas Souchu /* get data, ack interrupt line */
301e0efc557SJoerg Wunsch data = pcf_get_S0(sc);
3024e190a62SNicolas Souchu
3034e190a62SNicolas Souchu /* deliver the character */
304e0efc557SJoerg Wunsch iicbus_intr(sc->iicbus, INTR_RECEIVE, &data);
3054e190a62SNicolas Souchu }
3064e190a62SNicolas Souchu break;
3074e190a62SNicolas Souchu
3084e190a62SNicolas Souchu default:
3096e551fb6SDavid E. O'Brien panic("%s: unknown slave mode (%d)!", __func__,
310e0efc557SJoerg Wunsch sc->pcf_slave_mode);
3114e190a62SNicolas Souchu }
3124e190a62SNicolas Souchu
313e0efc557SJoerg Wunsch } while ((pcf_get_S1(sc) & PIN) == 0);
31413e3657bSJohn Baldwin PCF_UNLOCK(sc);
3154e190a62SNicolas Souchu
3164e190a62SNicolas Souchu return;
3174e190a62SNicolas Souchu
3184e190a62SNicolas Souchu error:
3194e190a62SNicolas Souchu /* unknown event on bus...reset PCF */
320e0efc557SJoerg Wunsch pcf_set_S1(sc, PIN|ESO|ENI|ACK);
3214e190a62SNicolas Souchu
322e0efc557SJoerg Wunsch sc->pcf_slave_mode = SLAVE_RECEIVER;
32313e3657bSJohn Baldwin PCF_UNLOCK(sc);
3244e190a62SNicolas Souchu
3254e190a62SNicolas Souchu return;
3264e190a62SNicolas Souchu }
3274e190a62SNicolas Souchu
328e0efc557SJoerg Wunsch int
pcf_rst_card(device_t dev,u_char speed,u_char addr,u_char * oldaddr)329e0efc557SJoerg Wunsch pcf_rst_card(device_t dev, u_char speed, u_char addr, u_char *oldaddr)
3304e190a62SNicolas Souchu {
331e0efc557SJoerg Wunsch struct pcf_softc *sc = DEVTOSOFTC(dev);
332af548787SNicolas Souchu
33313e3657bSJohn Baldwin PCF_LOCK(sc);
334af548787SNicolas Souchu if (oldaddr)
335e0efc557SJoerg Wunsch *oldaddr = sc->pcf_addr;
3364e190a62SNicolas Souchu
3374e190a62SNicolas Souchu /* retrieve own address from bus level */
338af548787SNicolas Souchu if (!addr)
339e0efc557SJoerg Wunsch sc->pcf_addr = PCF_DEFAULT_ADDR;
340af548787SNicolas Souchu else
341e0efc557SJoerg Wunsch sc->pcf_addr = addr;
3424e190a62SNicolas Souchu
343e0efc557SJoerg Wunsch pcf_set_S1(sc, PIN); /* initialize S1 */
3444e190a62SNicolas Souchu
3454e190a62SNicolas Souchu /* own address S'O<>0 */
346e0efc557SJoerg Wunsch pcf_set_S0(sc, sc->pcf_addr >> 1);
3474e190a62SNicolas Souchu
3484e190a62SNicolas Souchu /* select clock register */
349e0efc557SJoerg Wunsch pcf_set_S1(sc, PIN|ES1);
3504e190a62SNicolas Souchu
3514e190a62SNicolas Souchu /* select bus speed : 18=90kb, 19=45kb, 1A=11kb, 1B=1.5kb */
3524e190a62SNicolas Souchu switch (speed) {
3534e190a62SNicolas Souchu case IIC_SLOW:
354e0efc557SJoerg Wunsch pcf_set_S0(sc, 0x1b); /* XXX Sun uses 0x1f */
3554e190a62SNicolas Souchu break;
3564e190a62SNicolas Souchu
3574e190a62SNicolas Souchu case IIC_FAST:
358e0efc557SJoerg Wunsch pcf_set_S0(sc, 0x19); /* XXX Sun: 0x1d */
3594e190a62SNicolas Souchu break;
3604e190a62SNicolas Souchu
3614e190a62SNicolas Souchu case IIC_UNKNOWN:
3624e190a62SNicolas Souchu case IIC_FASTEST:
3634e190a62SNicolas Souchu default:
364e0efc557SJoerg Wunsch pcf_set_S0(sc, 0x18); /* XXX Sun: 0x1c */
3654e190a62SNicolas Souchu break;
3664e190a62SNicolas Souchu }
3674e190a62SNicolas Souchu
3684e190a62SNicolas Souchu /* set bus on, ack=yes, INT=yes */
369e0efc557SJoerg Wunsch pcf_set_S1(sc, PIN|ESO|ENI|ACK);
3704e190a62SNicolas Souchu
371e0efc557SJoerg Wunsch sc->pcf_slave_mode = SLAVE_RECEIVER;
37213e3657bSJohn Baldwin PCF_UNLOCK(sc);
3734e190a62SNicolas Souchu
3744e190a62SNicolas Souchu return (0);
3754e190a62SNicolas Souchu }
3764e190a62SNicolas Souchu
377e0efc557SJoerg Wunsch int
pcf_write(device_t dev,const char * buf,int len,int * sent,int timeout)3781c44eb75SAndriy Gapon pcf_write(device_t dev, const char *buf, int len, int *sent, int timeout /* us */)
3794e190a62SNicolas Souchu {
380e0efc557SJoerg Wunsch struct pcf_softc *sc = DEVTOSOFTC(dev);
3814e190a62SNicolas Souchu int bytes, error = 0;
3824e190a62SNicolas Souchu
3834e190a62SNicolas Souchu #ifdef PCFDEBUG
384e0efc557SJoerg Wunsch device_printf(dev, " >> writing %d bytes: %#x%s\n", len,
385e0efc557SJoerg Wunsch (unsigned)buf[0], len > 1? "...": "");
3864e190a62SNicolas Souchu #endif
3874e190a62SNicolas Souchu
3884e190a62SNicolas Souchu bytes = 0;
38913e3657bSJohn Baldwin PCF_LOCK(sc);
3904e190a62SNicolas Souchu while (len) {
391e0efc557SJoerg Wunsch pcf_set_S0(sc, *buf++);
3924e190a62SNicolas Souchu
393af548787SNicolas Souchu /* wait for the byte to be send */
394e0efc557SJoerg Wunsch if ((error = pcf_wait_byte(sc)))
3954e190a62SNicolas Souchu goto error;
3964e190a62SNicolas Souchu
397af548787SNicolas Souchu /* check if ack received */
398e0efc557SJoerg Wunsch if (pcf_noack(sc, timeout)) {
3994e190a62SNicolas Souchu error = IIC_ENOACK;
4004e190a62SNicolas Souchu goto error;
4014e190a62SNicolas Souchu }
4024e190a62SNicolas Souchu
4034e190a62SNicolas Souchu len --;
4044e190a62SNicolas Souchu bytes ++;
4054e190a62SNicolas Souchu }
4064e190a62SNicolas Souchu
4074e190a62SNicolas Souchu error:
4084e190a62SNicolas Souchu *sent = bytes;
40913e3657bSJohn Baldwin PCF_UNLOCK(sc);
4104e190a62SNicolas Souchu
4114e190a62SNicolas Souchu #ifdef PCFDEBUG
412e0efc557SJoerg Wunsch device_printf(dev, " >> %d bytes written (%d)\n", bytes, error);
4134e190a62SNicolas Souchu #endif
4144e190a62SNicolas Souchu
4154e190a62SNicolas Souchu return (error);
4164e190a62SNicolas Souchu }
4174e190a62SNicolas Souchu
418e0efc557SJoerg Wunsch int
pcf_read(device_t dev,char * buf,int len,int * read,int last,int delay)419e0efc557SJoerg Wunsch pcf_read(device_t dev, char *buf, int len, int *read, int last,
420af548787SNicolas Souchu int delay /* us */)
4214e190a62SNicolas Souchu {
422e0efc557SJoerg Wunsch struct pcf_softc *sc = DEVTOSOFTC(dev);
4234e190a62SNicolas Souchu int bytes, error = 0;
4244e190a62SNicolas Souchu #ifdef PCFDEBUG
425e0efc557SJoerg Wunsch char *obuf = buf;
426e0efc557SJoerg Wunsch
427e0efc557SJoerg Wunsch device_printf(dev, " << reading %d bytes\n", len);
4284e190a62SNicolas Souchu #endif
4294e190a62SNicolas Souchu
43013e3657bSJohn Baldwin PCF_LOCK(sc);
4314e190a62SNicolas Souchu /* trig the bus to get the first data byte in S0 */
4324e190a62SNicolas Souchu if (len) {
433af548787SNicolas Souchu if (len == 1 && last)
4344e190a62SNicolas Souchu /* just one byte to read */
435e0efc557SJoerg Wunsch pcf_set_S1(sc, ESO); /* no ack */
4364e190a62SNicolas Souchu
437e0efc557SJoerg Wunsch dummy_read(sc);
4384e190a62SNicolas Souchu }
4394e190a62SNicolas Souchu
4404e190a62SNicolas Souchu bytes = 0;
4414e190a62SNicolas Souchu while (len) {
442af548787SNicolas Souchu /* XXX delay needed here */
443af548787SNicolas Souchu
444af548787SNicolas Souchu /* wait for trigged byte */
445e0efc557SJoerg Wunsch if ((error = pcf_wait_byte(sc))) {
44613e3657bSJohn Baldwin pcf_stop_locked(sc);
4474e190a62SNicolas Souchu goto error;
4484e190a62SNicolas Souchu }
4494e190a62SNicolas Souchu
450af548787SNicolas Souchu if (len == 1 && last)
451af548787SNicolas Souchu /* ok, last data byte already in S0, no I2C activity
452e0efc557SJoerg Wunsch * on next pcf_get_S0() */
45313e3657bSJohn Baldwin pcf_stop_locked(sc);
4544e190a62SNicolas Souchu
455af548787SNicolas Souchu else if (len == 2 && last)
4564e190a62SNicolas Souchu /* next trigged byte with no ack */
457e0efc557SJoerg Wunsch pcf_set_S1(sc, ESO);
4584e190a62SNicolas Souchu
459af548787SNicolas Souchu /* receive byte, trig next byte */
460e0efc557SJoerg Wunsch *buf++ = pcf_get_S0(sc);
4614e190a62SNicolas Souchu
4624e190a62SNicolas Souchu len --;
4634e190a62SNicolas Souchu bytes ++;
46474b8d63dSPedro F. Giffuni }
4654e190a62SNicolas Souchu
4664e190a62SNicolas Souchu error:
4674e190a62SNicolas Souchu *read = bytes;
46813e3657bSJohn Baldwin PCF_UNLOCK(sc);
4694e190a62SNicolas Souchu
4704e190a62SNicolas Souchu #ifdef PCFDEBUG
471e0efc557SJoerg Wunsch device_printf(dev, " << %d bytes read (%d): %#x%s\n", bytes, error,
472e0efc557SJoerg Wunsch (unsigned)obuf[0], bytes > 1? "...": "");
4734e190a62SNicolas Souchu #endif
4744e190a62SNicolas Souchu
4754e190a62SNicolas Souchu return (error);
4764e190a62SNicolas Souchu }
4779e58d59fSJohn Baldwin
478676ea8e1SJohn Baldwin DRIVER_MODULE(iicbus, pcf, iicbus_driver, 0, 0);
4799e58d59fSJohn Baldwin MODULE_DEPEND(pcf, iicbus, PCF_MINVER, PCF_PREFVER, PCF_MAXVER);
4809e58d59fSJohn Baldwin MODULE_VERSION(pcf, PCF_MODVER);
481