xref: /freebsd/sys/dev/pcf/pcf.c (revision 9e58d59f4166a9fad46bb4587bf4807c8a7f2e93)
14e190a62SNicolas Souchu /*-
24e190a62SNicolas Souchu  * Copyright (c) 1998 Nicolas Souchu, Marc Bouget
3e0efc557SJoerg Wunsch  * Copyright (c) 2004 Joerg Wunsch
44e190a62SNicolas Souchu  * All rights reserved.
54e190a62SNicolas Souchu  *
64e190a62SNicolas Souchu  * Redistribution and use in source and binary forms, with or without
74e190a62SNicolas Souchu  * modification, are permitted provided that the following conditions
84e190a62SNicolas Souchu  * are met:
94e190a62SNicolas Souchu  * 1. Redistributions of source code must retain the above copyright
104e190a62SNicolas Souchu  *    notice, this list of conditions and the following disclaimer.
114e190a62SNicolas Souchu  * 2. Redistributions in binary form must reproduce the above copyright
124e190a62SNicolas Souchu  *    notice, this list of conditions and the following disclaimer in the
134e190a62SNicolas Souchu  *    documentation and/or other materials provided with the distribution.
144e190a62SNicolas Souchu  *
154e190a62SNicolas Souchu  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
164e190a62SNicolas Souchu  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
174e190a62SNicolas Souchu  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
184e190a62SNicolas Souchu  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
194e190a62SNicolas Souchu  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
204e190a62SNicolas Souchu  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
214e190a62SNicolas Souchu  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
224e190a62SNicolas Souchu  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
234e190a62SNicolas Souchu  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
244e190a62SNicolas Souchu  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
254e190a62SNicolas Souchu  * SUCH DAMAGE.
264e190a62SNicolas Souchu  */
27006124d8SDavid E. O'Brien 
28006124d8SDavid E. O'Brien #include <sys/cdefs.h>
29006124d8SDavid E. O'Brien __FBSDID("$FreeBSD$");
30006124d8SDavid E. O'Brien 
314e190a62SNicolas Souchu #include <sys/param.h>
3213e3657bSJohn Baldwin #include <sys/bus.h>
3313e3657bSJohn Baldwin #include <sys/lock.h>
344e190a62SNicolas Souchu #include <sys/kernel.h>
354e190a62SNicolas Souchu #include <sys/module.h>
3613e3657bSJohn Baldwin #include <sys/mutex.h>
3713e3657bSJohn Baldwin #include <sys/systm.h>
384e190a62SNicolas Souchu 
390f210c92SNicolas Souchu #include <machine/bus.h>
400f210c92SNicolas Souchu #include <machine/resource.h>
41e0efc557SJoerg Wunsch 
420f210c92SNicolas Souchu #include <sys/rman.h>
430f210c92SNicolas Souchu 
449e58d59fSJohn Baldwin #include <dev/iicbus/iicbus.h>
454e190a62SNicolas Souchu #include <dev/iicbus/iiconf.h>
46e0efc557SJoerg Wunsch #include <dev/pcf/pcfvar.h>
474e190a62SNicolas Souchu #include "iicbus_if.h"
484e190a62SNicolas Souchu 
49e0efc557SJoerg Wunsch /* Not so official debugging option. */
50e0efc557SJoerg Wunsch /* #define PCFDEBUG */
510f210c92SNicolas Souchu 
52e0efc557SJoerg Wunsch static int pcf_wait_byte(struct pcf_softc *pcf);
53e0efc557SJoerg Wunsch static int pcf_noack(struct pcf_softc *pcf, int timeout);
5413e3657bSJohn Baldwin static void pcf_stop_locked(struct pcf_softc *pcf);
554e190a62SNicolas Souchu 
564e190a62SNicolas Souchu /*
574e190a62SNicolas Souchu  * Polling mode for master operations wait for a new
5813e3657bSJohn Baldwin  * byte incoming or outgoing
594e190a62SNicolas Souchu  */
6013e3657bSJohn Baldwin static int
61e0efc557SJoerg Wunsch pcf_wait_byte(struct pcf_softc *sc)
624e190a62SNicolas Souchu {
634e190a62SNicolas Souchu 	int counter = TIMEOUT;
644e190a62SNicolas Souchu 
6513e3657bSJohn Baldwin 	PCF_ASSERT_LOCKED(sc);
664e190a62SNicolas Souchu 	while (counter--) {
674e190a62SNicolas Souchu 
68e0efc557SJoerg Wunsch 		if ((pcf_get_S1(sc) & PIN) == 0)
694e190a62SNicolas Souchu 			return (0);
704e190a62SNicolas Souchu 	}
714e190a62SNicolas Souchu 
724f16b8b1SNicolas Souchu #ifdef PCFDEBUG
734f16b8b1SNicolas Souchu 	printf("pcf: timeout!\n");
744f16b8b1SNicolas Souchu #endif
754f16b8b1SNicolas Souchu 
764e190a62SNicolas Souchu 	return (IIC_ETIMEOUT);
774e190a62SNicolas Souchu }
784e190a62SNicolas Souchu 
7913e3657bSJohn Baldwin static void
8013e3657bSJohn Baldwin pcf_stop_locked(struct pcf_softc *sc)
814e190a62SNicolas Souchu {
824e190a62SNicolas Souchu 
8313e3657bSJohn Baldwin 	PCF_ASSERT_LOCKED(sc);
84e0efc557SJoerg Wunsch #ifdef PCFDEBUG
85e0efc557SJoerg Wunsch 	device_printf(dev, " >> stop\n");
86e0efc557SJoerg Wunsch #endif
87af548787SNicolas Souchu 	/*
88af548787SNicolas Souchu 	 * Send STOP condition iff the START condition was previously sent.
899d5abbddSJens Schweikhardt 	 * STOP is sent only once even if an iicbus_stop() is called after
90e0efc557SJoerg Wunsch 	 * an iicbus_read()... see pcf_read(): the PCF needs to send the stop
91af548787SNicolas Souchu 	 * before the last char is read.
92af548787SNicolas Souchu 	 */
93e0efc557SJoerg Wunsch 	if (sc->pcf_started) {
944e190a62SNicolas Souchu 		/* set stop condition and enable IT */
95e0efc557SJoerg Wunsch 		pcf_set_S1(sc, PIN|ESO|ENI|STO|ACK);
964e190a62SNicolas Souchu 
97e0efc557SJoerg Wunsch 		sc->pcf_started = 0;
98af548787SNicolas Souchu 	}
994e190a62SNicolas Souchu }
1004e190a62SNicolas Souchu 
10113e3657bSJohn Baldwin static int
102e0efc557SJoerg Wunsch pcf_noack(struct pcf_softc *sc, int timeout)
103af548787SNicolas Souchu {
104af548787SNicolas Souchu 	int noack;
105af548787SNicolas Souchu 	int k = timeout/10;
106af548787SNicolas Souchu 
10713e3657bSJohn Baldwin 	PCF_ASSERT_LOCKED(sc);
108af548787SNicolas Souchu 	do {
109e0efc557SJoerg Wunsch 		noack = pcf_get_S1(sc) & LRB;
110af548787SNicolas Souchu 		if (!noack)
111af548787SNicolas Souchu 			break;
112af548787SNicolas Souchu 		DELAY(10);				/* XXX wait 10 us */
113af548787SNicolas Souchu 	} while (k--);
114af548787SNicolas Souchu 
115af548787SNicolas Souchu 	return (noack);
116af548787SNicolas Souchu }
117af548787SNicolas Souchu 
118e0efc557SJoerg Wunsch int
119e0efc557SJoerg Wunsch pcf_repeated_start(device_t dev, u_char slave, int timeout)
1204e190a62SNicolas Souchu {
121e0efc557SJoerg Wunsch 	struct pcf_softc *sc = DEVTOSOFTC(dev);
1224e190a62SNicolas Souchu 	int error = 0;
1234e190a62SNicolas Souchu 
12413e3657bSJohn Baldwin 	PCF_LOCK(sc);
125e0efc557SJoerg Wunsch #ifdef PCFDEBUG
126e0efc557SJoerg Wunsch 	device_printf(dev, " >> repeated start for slave %#x\n",
127e0efc557SJoerg Wunsch 		      (unsigned)slave);
128e0efc557SJoerg Wunsch #endif
1294e190a62SNicolas Souchu 	/* repeated start */
130e0efc557SJoerg Wunsch 	pcf_set_S1(sc, ESO|STA|STO|ACK);
1314e190a62SNicolas Souchu 
1324e190a62SNicolas Souchu 	/* set slave address to PCF. Last bit (LSB) must be set correctly
1334e190a62SNicolas Souchu 	 * according to transfer direction */
134e0efc557SJoerg Wunsch 	pcf_set_S0(sc, slave);
1354e190a62SNicolas Souchu 
1364e190a62SNicolas Souchu 	/* wait for address sent, polling */
137e0efc557SJoerg Wunsch 	if ((error = pcf_wait_byte(sc)))
1384e190a62SNicolas Souchu 		goto error;
1394e190a62SNicolas Souchu 
140af548787SNicolas Souchu 	/* check for ack */
141e0efc557SJoerg Wunsch 	if (pcf_noack(sc, timeout)) {
1424e190a62SNicolas Souchu 		error = IIC_ENOACK;
1434f16b8b1SNicolas Souchu #ifdef PCFDEBUG
1444f16b8b1SNicolas Souchu 		printf("pcf: no ack on repeated_start!\n");
1454f16b8b1SNicolas Souchu #endif
1464e190a62SNicolas Souchu 		goto error;
1474e190a62SNicolas Souchu 	}
1484e190a62SNicolas Souchu 
14913e3657bSJohn Baldwin 	PCF_UNLOCK(sc);
1504e190a62SNicolas Souchu 	return (0);
1514e190a62SNicolas Souchu 
1524e190a62SNicolas Souchu error:
15313e3657bSJohn Baldwin 	pcf_stop_locked(sc);
15413e3657bSJohn Baldwin 	PCF_UNLOCK(sc);
1554e190a62SNicolas Souchu 	return (error);
1564e190a62SNicolas Souchu }
1574e190a62SNicolas Souchu 
158e0efc557SJoerg Wunsch int
159e0efc557SJoerg Wunsch pcf_start(device_t dev, u_char slave, int timeout)
1604e190a62SNicolas Souchu {
161e0efc557SJoerg Wunsch 	struct pcf_softc *sc = DEVTOSOFTC(dev);
1624e190a62SNicolas Souchu 	int error = 0;
1634e190a62SNicolas Souchu 
16413e3657bSJohn Baldwin 	PCF_LOCK(sc);
165e0efc557SJoerg Wunsch #ifdef PCFDEBUG
166e0efc557SJoerg Wunsch 	device_printf(dev, " >> start for slave %#x\n", (unsigned)slave);
167e0efc557SJoerg Wunsch #endif
1684f16b8b1SNicolas Souchu 	if ((pcf_get_S1(sc) & nBB) == 0) {
1694f16b8b1SNicolas Souchu #ifdef PCFDEBUG
1704f16b8b1SNicolas Souchu 		printf("pcf: busy!\n");
1714f16b8b1SNicolas Souchu #endif
17213e3657bSJohn Baldwin 		PCF_UNLOCK(sc);
1734e190a62SNicolas Souchu 		return (IIC_EBUSBSY);
1744f16b8b1SNicolas Souchu 	}
1754e190a62SNicolas Souchu 
1764e190a62SNicolas Souchu 	/* set slave address to PCF. Last bit (LSB) must be set correctly
1774e190a62SNicolas Souchu 	 * according to transfer direction */
178e0efc557SJoerg Wunsch 	pcf_set_S0(sc, slave);
1794e190a62SNicolas Souchu 
1804e190a62SNicolas Souchu 	/* START only */
181e0efc557SJoerg Wunsch 	pcf_set_S1(sc, PIN|ESO|STA|ACK);
1824e190a62SNicolas Souchu 
183e0efc557SJoerg Wunsch 	sc->pcf_started = 1;
184af548787SNicolas Souchu 
1854e190a62SNicolas Souchu 	/* wait for address sent, polling */
186e0efc557SJoerg Wunsch 	if ((error = pcf_wait_byte(sc)))
1874e190a62SNicolas Souchu 		goto error;
1884e190a62SNicolas Souchu 
189af548787SNicolas Souchu 	/* check for ACK */
190e0efc557SJoerg Wunsch 	if (pcf_noack(sc, timeout)) {
1914e190a62SNicolas Souchu 		error = IIC_ENOACK;
1924f16b8b1SNicolas Souchu #ifdef PCFDEBUG
1934f16b8b1SNicolas Souchu 		printf("pcf: no ack on start!\n");
1944f16b8b1SNicolas Souchu #endif
1954e190a62SNicolas Souchu 		goto error;
1964e190a62SNicolas Souchu 	}
1974e190a62SNicolas Souchu 
19813e3657bSJohn Baldwin 	PCF_UNLOCK(sc);
1994e190a62SNicolas Souchu 	return (0);
2004e190a62SNicolas Souchu 
2014e190a62SNicolas Souchu error:
20213e3657bSJohn Baldwin 	pcf_stop_locked(sc);
20313e3657bSJohn Baldwin 	PCF_UNLOCK(sc);
2044e190a62SNicolas Souchu 	return (error);
2054e190a62SNicolas Souchu }
2064e190a62SNicolas Souchu 
20713e3657bSJohn Baldwin int
20813e3657bSJohn Baldwin pcf_stop(device_t dev)
20913e3657bSJohn Baldwin {
21013e3657bSJohn Baldwin 	struct pcf_softc *sc = DEVTOSOFTC(dev);
21113e3657bSJohn Baldwin 
21213e3657bSJohn Baldwin #ifdef PCFDEBUG
21313e3657bSJohn Baldwin 	device_printf(dev, " >> stop\n");
21413e3657bSJohn Baldwin #endif
21513e3657bSJohn Baldwin 	PCF_LOCK(sc);
21613e3657bSJohn Baldwin 	pcf_stop_locked(sc);
21713e3657bSJohn Baldwin 	PCF_UNLOCK(sc);
21813e3657bSJohn Baldwin 
21913e3657bSJohn Baldwin 	return (0);
22013e3657bSJohn Baldwin }
22113e3657bSJohn Baldwin 
222e0efc557SJoerg Wunsch void
223e0efc557SJoerg Wunsch pcf_intr(void *arg)
2244e190a62SNicolas Souchu {
2254f16b8b1SNicolas Souchu 	struct pcf_softc *sc = arg;
2264e190a62SNicolas Souchu 	char data, status, addr;
2274e190a62SNicolas Souchu 	char error = 0;
2284e190a62SNicolas Souchu 
22913e3657bSJohn Baldwin 	PCF_LOCK(sc);
230e0efc557SJoerg Wunsch 	status = pcf_get_S1(sc);
2314e190a62SNicolas Souchu 
2324e190a62SNicolas Souchu 	if (status & PIN) {
2334f16b8b1SNicolas Souchu 		printf("pcf: spurious interrupt, status=0x%x\n",
234e0efc557SJoerg Wunsch 		       status & 0xff);
2354e190a62SNicolas Souchu 
2364e190a62SNicolas Souchu 		goto error;
2374e190a62SNicolas Souchu 	}
2384e190a62SNicolas Souchu 
2394e190a62SNicolas Souchu 	if (status & LAB)
2404f16b8b1SNicolas Souchu 		printf("pcf: bus arbitration lost!\n");
2414e190a62SNicolas Souchu 
2424e190a62SNicolas Souchu 	if (status & BER) {
2434e190a62SNicolas Souchu 		error = IIC_EBUSERR;
244e0efc557SJoerg Wunsch 		iicbus_intr(sc->iicbus, INTR_ERROR, &error);
2454e190a62SNicolas Souchu 
2464e190a62SNicolas Souchu 		goto error;
2474e190a62SNicolas Souchu 	}
2484e190a62SNicolas Souchu 
2494e190a62SNicolas Souchu 	do {
250e0efc557SJoerg Wunsch 		status = pcf_get_S1(sc);
2514e190a62SNicolas Souchu 
252e0efc557SJoerg Wunsch 		switch(sc->pcf_slave_mode) {
2534e190a62SNicolas Souchu 
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
381e0efc557SJoerg Wunsch pcf_write(device_t dev, 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) {
3944e190a62SNicolas Souchu 
395e0efc557SJoerg Wunsch 		pcf_set_S0(sc, *buf++);
3964e190a62SNicolas Souchu 
397af548787SNicolas Souchu 		/* wait for the byte to be send */
398e0efc557SJoerg Wunsch 		if ((error = pcf_wait_byte(sc)))
3994e190a62SNicolas Souchu 			goto error;
4004e190a62SNicolas Souchu 
401af548787SNicolas Souchu 		/* check if ack received */
402e0efc557SJoerg Wunsch 		if (pcf_noack(sc, timeout)) {
4034e190a62SNicolas Souchu 			error = IIC_ENOACK;
4044e190a62SNicolas Souchu 			goto error;
4054e190a62SNicolas Souchu 		}
4064e190a62SNicolas Souchu 
4074e190a62SNicolas Souchu 		len --;
4084e190a62SNicolas Souchu 		bytes ++;
4094e190a62SNicolas Souchu 	}
4104e190a62SNicolas Souchu 
4114e190a62SNicolas Souchu error:
4124e190a62SNicolas Souchu 	*sent = bytes;
41313e3657bSJohn Baldwin 	PCF_UNLOCK(sc);
4144e190a62SNicolas Souchu 
4154e190a62SNicolas Souchu #ifdef PCFDEBUG
416e0efc557SJoerg Wunsch 	device_printf(dev, " >> %d bytes written (%d)\n", bytes, error);
4174e190a62SNicolas Souchu #endif
4184e190a62SNicolas Souchu 
4194e190a62SNicolas Souchu 	return (error);
4204e190a62SNicolas Souchu }
4214e190a62SNicolas Souchu 
422e0efc557SJoerg Wunsch int
423e0efc557SJoerg Wunsch pcf_read(device_t dev, char *buf, int len, int *read, int last,
424af548787SNicolas Souchu 	 int delay /* us */)
4254e190a62SNicolas Souchu {
426e0efc557SJoerg Wunsch 	struct pcf_softc *sc = DEVTOSOFTC(dev);
4274e190a62SNicolas Souchu 	int bytes, error = 0;
4284e190a62SNicolas Souchu #ifdef PCFDEBUG
429e0efc557SJoerg Wunsch 	char *obuf = buf;
430e0efc557SJoerg Wunsch 
431e0efc557SJoerg Wunsch 	device_printf(dev, " << reading %d bytes\n", len);
4324e190a62SNicolas Souchu #endif
4334e190a62SNicolas Souchu 
43413e3657bSJohn Baldwin 	PCF_LOCK(sc);
4354e190a62SNicolas Souchu 	/* trig the bus to get the first data byte in S0 */
4364e190a62SNicolas Souchu 	if (len) {
437af548787SNicolas Souchu 		if (len == 1 && last)
4384e190a62SNicolas Souchu 			/* just one byte to read */
439e0efc557SJoerg Wunsch 			pcf_set_S1(sc, ESO);		/* no ack */
4404e190a62SNicolas Souchu 
441e0efc557SJoerg Wunsch 		dummy_read(sc);
4424e190a62SNicolas Souchu 	}
4434e190a62SNicolas Souchu 
4444e190a62SNicolas Souchu 	bytes = 0;
4454e190a62SNicolas Souchu 	while (len) {
4464e190a62SNicolas Souchu 
447af548787SNicolas Souchu 		/* XXX delay needed here */
448af548787SNicolas Souchu 
449af548787SNicolas Souchu 		/* wait for trigged byte */
450e0efc557SJoerg Wunsch 		if ((error = pcf_wait_byte(sc))) {
45113e3657bSJohn Baldwin 			pcf_stop_locked(sc);
4524e190a62SNicolas Souchu 			goto error;
4534e190a62SNicolas Souchu 		}
4544e190a62SNicolas Souchu 
455af548787SNicolas Souchu 		if (len == 1 && last)
456af548787SNicolas Souchu 			/* ok, last data byte already in S0, no I2C activity
457e0efc557SJoerg Wunsch 			 * on next pcf_get_S0() */
45813e3657bSJohn Baldwin 			pcf_stop_locked(sc);
4594e190a62SNicolas Souchu 
460af548787SNicolas Souchu 		else if (len == 2 && last)
4614e190a62SNicolas Souchu 			/* next trigged byte with no ack */
462e0efc557SJoerg Wunsch 			pcf_set_S1(sc, ESO);
4634e190a62SNicolas Souchu 
464af548787SNicolas Souchu 		/* receive byte, trig next byte */
465e0efc557SJoerg Wunsch 		*buf++ = pcf_get_S0(sc);
4664e190a62SNicolas Souchu 
4674e190a62SNicolas Souchu 		len --;
4684e190a62SNicolas Souchu 		bytes ++;
4694e190a62SNicolas Souchu 	};
4704e190a62SNicolas Souchu 
4714e190a62SNicolas Souchu error:
4724e190a62SNicolas Souchu 	*read = bytes;
47313e3657bSJohn Baldwin 	PCF_UNLOCK(sc);
4744e190a62SNicolas Souchu 
4754e190a62SNicolas Souchu #ifdef PCFDEBUG
476e0efc557SJoerg Wunsch 	device_printf(dev, " << %d bytes read (%d): %#x%s\n", bytes, error,
477e0efc557SJoerg Wunsch 		      (unsigned)obuf[0], bytes > 1? "...": "");
4784e190a62SNicolas Souchu #endif
4794e190a62SNicolas Souchu 
4804e190a62SNicolas Souchu 	return (error);
4814e190a62SNicolas Souchu }
4829e58d59fSJohn Baldwin 
4839e58d59fSJohn Baldwin DRIVER_MODULE(iicbus, pcf, iicbus_driver, iicbus_devclass, 0, 0);
4849e58d59fSJohn Baldwin MODULE_DEPEND(pcf, iicbus, PCF_MINVER, PCF_PREFVER, PCF_MAXVER);
4859e58d59fSJohn Baldwin MODULE_VERSION(pcf, PCF_MODVER);
486