xref: /freebsd/sys/dev/pcf/pcf.c (revision 676ea8e1778d671703c8901ea82ad4092b00b24a)
14e190a62SNicolas Souchu /*-
2718cf2ccSPedro F. Giffuni  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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 
30006124d8SDavid E. O'Brien #include <sys/cdefs.h>
31006124d8SDavid E. O'Brien __FBSDID("$FreeBSD$");
32006124d8SDavid E. O'Brien 
334e190a62SNicolas Souchu #include <sys/param.h>
3413e3657bSJohn Baldwin #include <sys/bus.h>
3513e3657bSJohn Baldwin #include <sys/lock.h>
364e190a62SNicolas Souchu #include <sys/kernel.h>
374e190a62SNicolas Souchu #include <sys/module.h>
3813e3657bSJohn Baldwin #include <sys/mutex.h>
3913e3657bSJohn Baldwin #include <sys/systm.h>
404e190a62SNicolas Souchu 
410f210c92SNicolas Souchu #include <machine/bus.h>
420f210c92SNicolas Souchu #include <machine/resource.h>
43e0efc557SJoerg Wunsch 
440f210c92SNicolas Souchu #include <sys/rman.h>
450f210c92SNicolas Souchu 
469e58d59fSJohn Baldwin #include <dev/iicbus/iicbus.h>
474e190a62SNicolas Souchu #include <dev/iicbus/iiconf.h>
48e0efc557SJoerg Wunsch #include <dev/pcf/pcfvar.h>
494e190a62SNicolas Souchu #include "iicbus_if.h"
504e190a62SNicolas Souchu 
51e0efc557SJoerg Wunsch /* Not so official debugging option. */
52e0efc557SJoerg Wunsch /* #define PCFDEBUG */
530f210c92SNicolas Souchu 
54e0efc557SJoerg Wunsch static int pcf_wait_byte(struct pcf_softc *pcf);
55e0efc557SJoerg Wunsch static int pcf_noack(struct pcf_softc *pcf, int timeout);
5613e3657bSJohn Baldwin static void pcf_stop_locked(struct pcf_softc *pcf);
574e190a62SNicolas Souchu 
584e190a62SNicolas Souchu /*
594e190a62SNicolas Souchu  * Polling mode for master operations wait for a new
6013e3657bSJohn Baldwin  * byte incoming or outgoing
614e190a62SNicolas Souchu  */
6213e3657bSJohn Baldwin static int
63e0efc557SJoerg Wunsch pcf_wait_byte(struct pcf_softc *sc)
644e190a62SNicolas Souchu {
654e190a62SNicolas Souchu 	int counter = TIMEOUT;
664e190a62SNicolas Souchu 
6713e3657bSJohn Baldwin 	PCF_ASSERT_LOCKED(sc);
684e190a62SNicolas Souchu 	while (counter--) {
69e0efc557SJoerg Wunsch 		if ((pcf_get_S1(sc) & PIN) == 0)
704e190a62SNicolas Souchu 			return (0);
714e190a62SNicolas Souchu 	}
724e190a62SNicolas Souchu 
734f16b8b1SNicolas Souchu #ifdef PCFDEBUG
744f16b8b1SNicolas Souchu 	printf("pcf: timeout!\n");
754f16b8b1SNicolas Souchu #endif
764f16b8b1SNicolas Souchu 
774e190a62SNicolas Souchu 	return (IIC_ETIMEOUT);
784e190a62SNicolas Souchu }
794e190a62SNicolas Souchu 
8013e3657bSJohn Baldwin static void
8113e3657bSJohn Baldwin pcf_stop_locked(struct pcf_softc *sc)
824e190a62SNicolas Souchu {
834e190a62SNicolas Souchu 
8413e3657bSJohn Baldwin 	PCF_ASSERT_LOCKED(sc);
85e0efc557SJoerg Wunsch #ifdef PCFDEBUG
86e0efc557SJoerg Wunsch 	device_printf(dev, " >> stop\n");
87e0efc557SJoerg Wunsch #endif
88af548787SNicolas Souchu 	/*
89af548787SNicolas Souchu 	 * Send STOP condition iff the START condition was previously sent.
909d5abbddSJens Schweikhardt 	 * STOP is sent only once even if an iicbus_stop() is called after
91e0efc557SJoerg Wunsch 	 * an iicbus_read()... see pcf_read(): the PCF needs to send the stop
92af548787SNicolas Souchu 	 * before the last char is read.
93af548787SNicolas Souchu 	 */
94e0efc557SJoerg Wunsch 	if (sc->pcf_started) {
954e190a62SNicolas Souchu 		/* set stop condition and enable IT */
96e0efc557SJoerg Wunsch 		pcf_set_S1(sc, PIN|ESO|ENI|STO|ACK);
974e190a62SNicolas Souchu 
98e0efc557SJoerg Wunsch 		sc->pcf_started = 0;
99af548787SNicolas Souchu 	}
1004e190a62SNicolas Souchu }
1014e190a62SNicolas Souchu 
10213e3657bSJohn Baldwin static int
103e0efc557SJoerg Wunsch pcf_noack(struct pcf_softc *sc, int timeout)
104af548787SNicolas Souchu {
105af548787SNicolas Souchu 	int noack;
106af548787SNicolas Souchu 	int k = timeout/10;
107af548787SNicolas Souchu 
10813e3657bSJohn Baldwin 	PCF_ASSERT_LOCKED(sc);
109af548787SNicolas Souchu 	do {
110e0efc557SJoerg Wunsch 		noack = pcf_get_S1(sc) & LRB;
111af548787SNicolas Souchu 		if (!noack)
112af548787SNicolas Souchu 			break;
113af548787SNicolas Souchu 		DELAY(10);				/* XXX wait 10 us */
114af548787SNicolas Souchu 	} while (k--);
115af548787SNicolas Souchu 
116af548787SNicolas Souchu 	return (noack);
117af548787SNicolas Souchu }
118af548787SNicolas Souchu 
119e0efc557SJoerg Wunsch int
120e0efc557SJoerg Wunsch pcf_repeated_start(device_t dev, u_char slave, int timeout)
1214e190a62SNicolas Souchu {
122e0efc557SJoerg Wunsch 	struct pcf_softc *sc = DEVTOSOFTC(dev);
1234e190a62SNicolas Souchu 	int error = 0;
1244e190a62SNicolas Souchu 
12513e3657bSJohn Baldwin 	PCF_LOCK(sc);
126e0efc557SJoerg Wunsch #ifdef PCFDEBUG
127e0efc557SJoerg Wunsch 	device_printf(dev, " >> repeated start for slave %#x\n",
128e0efc557SJoerg Wunsch 		      (unsigned)slave);
129e0efc557SJoerg Wunsch #endif
1304e190a62SNicolas Souchu 	/* repeated start */
131e0efc557SJoerg Wunsch 	pcf_set_S1(sc, ESO|STA|STO|ACK);
1324e190a62SNicolas Souchu 
1334e190a62SNicolas Souchu 	/* set slave address to PCF. Last bit (LSB) must be set correctly
1344e190a62SNicolas Souchu 	 * according to transfer direction */
135e0efc557SJoerg Wunsch 	pcf_set_S0(sc, slave);
1364e190a62SNicolas Souchu 
1374e190a62SNicolas Souchu 	/* wait for address sent, polling */
138e0efc557SJoerg Wunsch 	if ((error = pcf_wait_byte(sc)))
1394e190a62SNicolas Souchu 		goto error;
1404e190a62SNicolas Souchu 
141af548787SNicolas Souchu 	/* check for ack */
142e0efc557SJoerg Wunsch 	if (pcf_noack(sc, timeout)) {
1434e190a62SNicolas Souchu 		error = IIC_ENOACK;
1444f16b8b1SNicolas Souchu #ifdef PCFDEBUG
1454f16b8b1SNicolas Souchu 		printf("pcf: no ack on repeated_start!\n");
1464f16b8b1SNicolas Souchu #endif
1474e190a62SNicolas Souchu 		goto error;
1484e190a62SNicolas Souchu 	}
1494e190a62SNicolas Souchu 
15013e3657bSJohn Baldwin 	PCF_UNLOCK(sc);
1514e190a62SNicolas Souchu 	return (0);
1524e190a62SNicolas Souchu 
1534e190a62SNicolas Souchu error:
15413e3657bSJohn Baldwin 	pcf_stop_locked(sc);
15513e3657bSJohn Baldwin 	PCF_UNLOCK(sc);
1564e190a62SNicolas Souchu 	return (error);
1574e190a62SNicolas Souchu }
1584e190a62SNicolas Souchu 
159e0efc557SJoerg Wunsch int
160e0efc557SJoerg Wunsch pcf_start(device_t dev, u_char slave, int timeout)
1614e190a62SNicolas Souchu {
162e0efc557SJoerg Wunsch 	struct pcf_softc *sc = DEVTOSOFTC(dev);
1634e190a62SNicolas Souchu 	int error = 0;
1644e190a62SNicolas Souchu 
16513e3657bSJohn Baldwin 	PCF_LOCK(sc);
166e0efc557SJoerg Wunsch #ifdef PCFDEBUG
167e0efc557SJoerg Wunsch 	device_printf(dev, " >> start for slave %#x\n", (unsigned)slave);
168e0efc557SJoerg Wunsch #endif
1694f16b8b1SNicolas Souchu 	if ((pcf_get_S1(sc) & nBB) == 0) {
1704f16b8b1SNicolas Souchu #ifdef PCFDEBUG
1714f16b8b1SNicolas Souchu 		printf("pcf: busy!\n");
1724f16b8b1SNicolas Souchu #endif
17313e3657bSJohn Baldwin 		PCF_UNLOCK(sc);
174d1e99670SIan Lepore 		return (IIC_EBUSERR);
1754f16b8b1SNicolas Souchu 	}
1764e190a62SNicolas Souchu 
1774e190a62SNicolas Souchu 	/* set slave address to PCF. Last bit (LSB) must be set correctly
1784e190a62SNicolas Souchu 	 * according to transfer direction */
179e0efc557SJoerg Wunsch 	pcf_set_S0(sc, slave);
1804e190a62SNicolas Souchu 
1814e190a62SNicolas Souchu 	/* START only */
182e0efc557SJoerg Wunsch 	pcf_set_S1(sc, PIN|ESO|STA|ACK);
1834e190a62SNicolas Souchu 
184e0efc557SJoerg Wunsch 	sc->pcf_started = 1;
185af548787SNicolas Souchu 
1864e190a62SNicolas Souchu 	/* wait for address sent, polling */
187e0efc557SJoerg Wunsch 	if ((error = pcf_wait_byte(sc)))
1884e190a62SNicolas Souchu 		goto error;
1894e190a62SNicolas Souchu 
190af548787SNicolas Souchu 	/* check for ACK */
191e0efc557SJoerg Wunsch 	if (pcf_noack(sc, timeout)) {
1924e190a62SNicolas Souchu 		error = IIC_ENOACK;
1934f16b8b1SNicolas Souchu #ifdef PCFDEBUG
1944f16b8b1SNicolas Souchu 		printf("pcf: no ack on start!\n");
1954f16b8b1SNicolas Souchu #endif
1964e190a62SNicolas Souchu 		goto error;
1974e190a62SNicolas Souchu 	}
1984e190a62SNicolas Souchu 
19913e3657bSJohn Baldwin 	PCF_UNLOCK(sc);
2004e190a62SNicolas Souchu 	return (0);
2014e190a62SNicolas Souchu 
2024e190a62SNicolas Souchu error:
20313e3657bSJohn Baldwin 	pcf_stop_locked(sc);
20413e3657bSJohn Baldwin 	PCF_UNLOCK(sc);
2054e190a62SNicolas Souchu 	return (error);
2064e190a62SNicolas Souchu }
2074e190a62SNicolas Souchu 
20813e3657bSJohn Baldwin int
20913e3657bSJohn Baldwin pcf_stop(device_t dev)
21013e3657bSJohn Baldwin {
21113e3657bSJohn Baldwin 	struct pcf_softc *sc = DEVTOSOFTC(dev);
21213e3657bSJohn Baldwin 
21313e3657bSJohn Baldwin #ifdef PCFDEBUG
21413e3657bSJohn Baldwin 	device_printf(dev, " >> stop\n");
21513e3657bSJohn Baldwin #endif
21613e3657bSJohn Baldwin 	PCF_LOCK(sc);
21713e3657bSJohn Baldwin 	pcf_stop_locked(sc);
21813e3657bSJohn Baldwin 	PCF_UNLOCK(sc);
21913e3657bSJohn Baldwin 
22013e3657bSJohn Baldwin 	return (0);
22113e3657bSJohn Baldwin }
22213e3657bSJohn Baldwin 
223e0efc557SJoerg Wunsch void
224e0efc557SJoerg Wunsch pcf_intr(void *arg)
2254e190a62SNicolas Souchu {
2264f16b8b1SNicolas Souchu 	struct pcf_softc *sc = arg;
2274e190a62SNicolas Souchu 	char data, status, addr;
2284e190a62SNicolas Souchu 	char error = 0;
2294e190a62SNicolas Souchu 
23013e3657bSJohn Baldwin 	PCF_LOCK(sc);
231e0efc557SJoerg Wunsch 	status = pcf_get_S1(sc);
2324e190a62SNicolas Souchu 
2334e190a62SNicolas Souchu 	if (status & PIN) {
2344f16b8b1SNicolas Souchu 		printf("pcf: spurious interrupt, status=0x%x\n",
235e0efc557SJoerg Wunsch 		       status & 0xff);
2364e190a62SNicolas Souchu 
2374e190a62SNicolas Souchu 		goto error;
2384e190a62SNicolas Souchu 	}
2394e190a62SNicolas Souchu 
2404e190a62SNicolas Souchu 	if (status & LAB)
2414f16b8b1SNicolas Souchu 		printf("pcf: bus arbitration lost!\n");
2424e190a62SNicolas Souchu 
2434e190a62SNicolas Souchu 	if (status & BER) {
2444e190a62SNicolas Souchu 		error = IIC_EBUSERR;
245e0efc557SJoerg Wunsch 		iicbus_intr(sc->iicbus, INTR_ERROR, &error);
2464e190a62SNicolas Souchu 
2474e190a62SNicolas Souchu 		goto error;
2484e190a62SNicolas Souchu 	}
2494e190a62SNicolas Souchu 
2504e190a62SNicolas Souchu 	do {
251e0efc557SJoerg Wunsch 		status = pcf_get_S1(sc);
2524e190a62SNicolas Souchu 
253e0efc557SJoerg Wunsch 		switch(sc->pcf_slave_mode) {
2544e190a62SNicolas Souchu 		case SLAVE_TRANSMITTER:
2554e190a62SNicolas Souchu 			if (status & LRB) {
2564e190a62SNicolas Souchu 				/* ack interrupt line */
257e0efc557SJoerg Wunsch 				dummy_write(sc);
2584e190a62SNicolas Souchu 
2594e190a62SNicolas Souchu 				/* no ack, don't send anymore */
260e0efc557SJoerg Wunsch 				sc->pcf_slave_mode = SLAVE_RECEIVER;
2614e190a62SNicolas Souchu 
262e0efc557SJoerg Wunsch 				iicbus_intr(sc->iicbus, INTR_NOACK, NULL);
2634e190a62SNicolas Souchu 				break;
2644e190a62SNicolas Souchu 			}
2654e190a62SNicolas Souchu 
2664e190a62SNicolas Souchu 			/* get data from upper code */
267e0efc557SJoerg Wunsch 			iicbus_intr(sc->iicbus, INTR_TRANSMIT, &data);
2684e190a62SNicolas Souchu 
269e0efc557SJoerg Wunsch 			pcf_set_S0(sc, data);
2704e190a62SNicolas Souchu 			break;
2714e190a62SNicolas Souchu 
2724e190a62SNicolas Souchu 		case SLAVE_RECEIVER:
2734e190a62SNicolas Souchu 			if (status & AAS) {
274e0efc557SJoerg Wunsch 				addr = pcf_get_S0(sc);
2754e190a62SNicolas Souchu 
2764e190a62SNicolas Souchu 				if (status & AD0)
277e0efc557SJoerg Wunsch 					iicbus_intr(sc->iicbus, INTR_GENERAL, &addr);
2784e190a62SNicolas Souchu 				else
279e0efc557SJoerg Wunsch 					iicbus_intr(sc->iicbus, INTR_START, &addr);
2804e190a62SNicolas Souchu 
2814e190a62SNicolas Souchu 				if (addr & LSB) {
282e0efc557SJoerg Wunsch 					sc->pcf_slave_mode = SLAVE_TRANSMITTER;
2834e190a62SNicolas Souchu 
2844e190a62SNicolas Souchu 					/* get the first char from upper code */
285e0efc557SJoerg Wunsch 					iicbus_intr(sc->iicbus, INTR_TRANSMIT, &data);
2864e190a62SNicolas Souchu 
2874e190a62SNicolas Souchu 					/* send first data byte */
288e0efc557SJoerg Wunsch 					pcf_set_S0(sc, data);
2894e190a62SNicolas Souchu 				}
2904e190a62SNicolas Souchu 
2914e190a62SNicolas Souchu 				break;
2924e190a62SNicolas Souchu 			}
2934e190a62SNicolas Souchu 
2944e190a62SNicolas Souchu 			/* stop condition received? */
2954e190a62SNicolas Souchu 			if (status & STS) {
2964e190a62SNicolas Souchu 				/* ack interrupt line */
297e0efc557SJoerg Wunsch 				dummy_read(sc);
2984e190a62SNicolas Souchu 
2994e190a62SNicolas Souchu 				/* emulate intr stop condition */
300e0efc557SJoerg Wunsch 				iicbus_intr(sc->iicbus, INTR_STOP, NULL);
3014e190a62SNicolas Souchu 
3024e190a62SNicolas Souchu 			} else {
3034e190a62SNicolas Souchu 				/* get data, ack interrupt line */
304e0efc557SJoerg Wunsch 				data = pcf_get_S0(sc);
3054e190a62SNicolas Souchu 
3064e190a62SNicolas Souchu 				/* deliver the character */
307e0efc557SJoerg Wunsch 				iicbus_intr(sc->iicbus, INTR_RECEIVE, &data);
3084e190a62SNicolas Souchu 			}
3094e190a62SNicolas Souchu 			break;
3104e190a62SNicolas Souchu 
3114e190a62SNicolas Souchu 		    default:
3126e551fb6SDavid E. O'Brien 			panic("%s: unknown slave mode (%d)!", __func__,
313e0efc557SJoerg Wunsch 				sc->pcf_slave_mode);
3144e190a62SNicolas Souchu 		    }
3154e190a62SNicolas Souchu 
316e0efc557SJoerg Wunsch 	} while ((pcf_get_S1(sc) & PIN) == 0);
31713e3657bSJohn Baldwin 	PCF_UNLOCK(sc);
3184e190a62SNicolas Souchu 
3194e190a62SNicolas Souchu 	return;
3204e190a62SNicolas Souchu 
3214e190a62SNicolas Souchu error:
3224e190a62SNicolas Souchu 	/* unknown event on bus...reset PCF */
323e0efc557SJoerg Wunsch 	pcf_set_S1(sc, PIN|ESO|ENI|ACK);
3244e190a62SNicolas Souchu 
325e0efc557SJoerg Wunsch 	sc->pcf_slave_mode = SLAVE_RECEIVER;
32613e3657bSJohn Baldwin 	PCF_UNLOCK(sc);
3274e190a62SNicolas Souchu 
3284e190a62SNicolas Souchu 	return;
3294e190a62SNicolas Souchu }
3304e190a62SNicolas Souchu 
331e0efc557SJoerg Wunsch int
332e0efc557SJoerg Wunsch pcf_rst_card(device_t dev, u_char speed, u_char addr, u_char *oldaddr)
3334e190a62SNicolas Souchu {
334e0efc557SJoerg Wunsch 	struct pcf_softc *sc = DEVTOSOFTC(dev);
335af548787SNicolas Souchu 
33613e3657bSJohn Baldwin 	PCF_LOCK(sc);
337af548787SNicolas Souchu 	if (oldaddr)
338e0efc557SJoerg Wunsch 		*oldaddr = sc->pcf_addr;
3394e190a62SNicolas Souchu 
3404e190a62SNicolas Souchu 	/* retrieve own address from bus level */
341af548787SNicolas Souchu 	if (!addr)
342e0efc557SJoerg Wunsch 		sc->pcf_addr = PCF_DEFAULT_ADDR;
343af548787SNicolas Souchu 	else
344e0efc557SJoerg Wunsch 		sc->pcf_addr = addr;
3454e190a62SNicolas Souchu 
346e0efc557SJoerg Wunsch 	pcf_set_S1(sc, PIN);				/* initialize S1 */
3474e190a62SNicolas Souchu 
3484e190a62SNicolas Souchu 	/* own address S'O<>0 */
349e0efc557SJoerg Wunsch 	pcf_set_S0(sc, sc->pcf_addr >> 1);
3504e190a62SNicolas Souchu 
3514e190a62SNicolas Souchu 	/* select clock register */
352e0efc557SJoerg Wunsch 	pcf_set_S1(sc, PIN|ES1);
3534e190a62SNicolas Souchu 
3544e190a62SNicolas Souchu 	/* select bus speed : 18=90kb, 19=45kb, 1A=11kb, 1B=1.5kb */
3554e190a62SNicolas Souchu 	switch (speed) {
3564e190a62SNicolas Souchu 	case IIC_SLOW:
357e0efc557SJoerg Wunsch 		pcf_set_S0(sc,  0x1b); /* XXX Sun uses 0x1f */
3584e190a62SNicolas Souchu 		break;
3594e190a62SNicolas Souchu 
3604e190a62SNicolas Souchu 	case IIC_FAST:
361e0efc557SJoerg Wunsch 		pcf_set_S0(sc,  0x19); /* XXX Sun: 0x1d */
3624e190a62SNicolas Souchu 		break;
3634e190a62SNicolas Souchu 
3644e190a62SNicolas Souchu 	case IIC_UNKNOWN:
3654e190a62SNicolas Souchu 	case IIC_FASTEST:
3664e190a62SNicolas Souchu 	default:
367e0efc557SJoerg Wunsch 		pcf_set_S0(sc,  0x18); /* XXX Sun: 0x1c */
3684e190a62SNicolas Souchu 		break;
3694e190a62SNicolas Souchu 	}
3704e190a62SNicolas Souchu 
3714e190a62SNicolas Souchu 	/* set bus on, ack=yes, INT=yes */
372e0efc557SJoerg Wunsch 	pcf_set_S1(sc, PIN|ESO|ENI|ACK);
3734e190a62SNicolas Souchu 
374e0efc557SJoerg Wunsch 	sc->pcf_slave_mode = SLAVE_RECEIVER;
37513e3657bSJohn Baldwin 	PCF_UNLOCK(sc);
3764e190a62SNicolas Souchu 
3774e190a62SNicolas Souchu 	return (0);
3784e190a62SNicolas Souchu }
3794e190a62SNicolas Souchu 
380e0efc557SJoerg Wunsch int
3811c44eb75SAndriy Gapon pcf_write(device_t dev, const char *buf, int len, int *sent, int timeout /* us */)
3824e190a62SNicolas Souchu {
383e0efc557SJoerg Wunsch 	struct pcf_softc *sc = DEVTOSOFTC(dev);
3844e190a62SNicolas Souchu 	int bytes, error = 0;
3854e190a62SNicolas Souchu 
3864e190a62SNicolas Souchu #ifdef PCFDEBUG
387e0efc557SJoerg Wunsch 	device_printf(dev, " >> writing %d bytes: %#x%s\n", len,
388e0efc557SJoerg Wunsch 		      (unsigned)buf[0], len > 1? "...": "");
3894e190a62SNicolas Souchu #endif
3904e190a62SNicolas Souchu 
3914e190a62SNicolas Souchu 	bytes = 0;
39213e3657bSJohn Baldwin 	PCF_LOCK(sc);
3934e190a62SNicolas Souchu 	while (len) {
394e0efc557SJoerg Wunsch 		pcf_set_S0(sc, *buf++);
3954e190a62SNicolas Souchu 
396af548787SNicolas Souchu 		/* wait for the byte to be send */
397e0efc557SJoerg Wunsch 		if ((error = pcf_wait_byte(sc)))
3984e190a62SNicolas Souchu 			goto error;
3994e190a62SNicolas Souchu 
400af548787SNicolas Souchu 		/* check if ack received */
401e0efc557SJoerg Wunsch 		if (pcf_noack(sc, timeout)) {
4024e190a62SNicolas Souchu 			error = IIC_ENOACK;
4034e190a62SNicolas Souchu 			goto error;
4044e190a62SNicolas Souchu 		}
4054e190a62SNicolas Souchu 
4064e190a62SNicolas Souchu 		len --;
4074e190a62SNicolas Souchu 		bytes ++;
4084e190a62SNicolas Souchu 	}
4094e190a62SNicolas Souchu 
4104e190a62SNicolas Souchu error:
4114e190a62SNicolas Souchu 	*sent = bytes;
41213e3657bSJohn Baldwin 	PCF_UNLOCK(sc);
4134e190a62SNicolas Souchu 
4144e190a62SNicolas Souchu #ifdef PCFDEBUG
415e0efc557SJoerg Wunsch 	device_printf(dev, " >> %d bytes written (%d)\n", bytes, error);
4164e190a62SNicolas Souchu #endif
4174e190a62SNicolas Souchu 
4184e190a62SNicolas Souchu 	return (error);
4194e190a62SNicolas Souchu }
4204e190a62SNicolas Souchu 
421e0efc557SJoerg Wunsch int
422e0efc557SJoerg Wunsch pcf_read(device_t dev, char *buf, int len, int *read, int last,
423af548787SNicolas Souchu 	 int delay /* us */)
4244e190a62SNicolas Souchu {
425e0efc557SJoerg Wunsch 	struct pcf_softc *sc = DEVTOSOFTC(dev);
4264e190a62SNicolas Souchu 	int bytes, error = 0;
4274e190a62SNicolas Souchu #ifdef PCFDEBUG
428e0efc557SJoerg Wunsch 	char *obuf = buf;
429e0efc557SJoerg Wunsch 
430e0efc557SJoerg Wunsch 	device_printf(dev, " << reading %d bytes\n", len);
4314e190a62SNicolas Souchu #endif
4324e190a62SNicolas Souchu 
43313e3657bSJohn Baldwin 	PCF_LOCK(sc);
4344e190a62SNicolas Souchu 	/* trig the bus to get the first data byte in S0 */
4354e190a62SNicolas Souchu 	if (len) {
436af548787SNicolas Souchu 		if (len == 1 && last)
4374e190a62SNicolas Souchu 			/* just one byte to read */
438e0efc557SJoerg Wunsch 			pcf_set_S1(sc, ESO);		/* no ack */
4394e190a62SNicolas Souchu 
440e0efc557SJoerg Wunsch 		dummy_read(sc);
4414e190a62SNicolas Souchu 	}
4424e190a62SNicolas Souchu 
4434e190a62SNicolas Souchu 	bytes = 0;
4444e190a62SNicolas Souchu 	while (len) {
445af548787SNicolas Souchu 		/* XXX delay needed here */
446af548787SNicolas Souchu 
447af548787SNicolas Souchu 		/* wait for trigged byte */
448e0efc557SJoerg Wunsch 		if ((error = pcf_wait_byte(sc))) {
44913e3657bSJohn Baldwin 			pcf_stop_locked(sc);
4504e190a62SNicolas Souchu 			goto error;
4514e190a62SNicolas Souchu 		}
4524e190a62SNicolas Souchu 
453af548787SNicolas Souchu 		if (len == 1 && last)
454af548787SNicolas Souchu 			/* ok, last data byte already in S0, no I2C activity
455e0efc557SJoerg Wunsch 			 * on next pcf_get_S0() */
45613e3657bSJohn Baldwin 			pcf_stop_locked(sc);
4574e190a62SNicolas Souchu 
458af548787SNicolas Souchu 		else if (len == 2 && last)
4594e190a62SNicolas Souchu 			/* next trigged byte with no ack */
460e0efc557SJoerg Wunsch 			pcf_set_S1(sc, ESO);
4614e190a62SNicolas Souchu 
462af548787SNicolas Souchu 		/* receive byte, trig next byte */
463e0efc557SJoerg Wunsch 		*buf++ = pcf_get_S0(sc);
4644e190a62SNicolas Souchu 
4654e190a62SNicolas Souchu 		len --;
4664e190a62SNicolas Souchu 		bytes ++;
46774b8d63dSPedro F. Giffuni 	}
4684e190a62SNicolas Souchu 
4694e190a62SNicolas Souchu error:
4704e190a62SNicolas Souchu 	*read = bytes;
47113e3657bSJohn Baldwin 	PCF_UNLOCK(sc);
4724e190a62SNicolas Souchu 
4734e190a62SNicolas Souchu #ifdef PCFDEBUG
474e0efc557SJoerg Wunsch 	device_printf(dev, " << %d bytes read (%d): %#x%s\n", bytes, error,
475e0efc557SJoerg Wunsch 		      (unsigned)obuf[0], bytes > 1? "...": "");
4764e190a62SNicolas Souchu #endif
4774e190a62SNicolas Souchu 
4784e190a62SNicolas Souchu 	return (error);
4794e190a62SNicolas Souchu }
4809e58d59fSJohn Baldwin 
481*676ea8e1SJohn Baldwin DRIVER_MODULE(iicbus, pcf, iicbus_driver, 0, 0);
4829e58d59fSJohn Baldwin MODULE_DEPEND(pcf, iicbus, PCF_MINVER, PCF_PREFVER, PCF_MAXVER);
4839e58d59fSJohn Baldwin MODULE_VERSION(pcf, PCF_MODVER);
484