xref: /freebsd/sys/dev/pcf/pcf.c (revision fdafd315ad0d0f28a11b9fb4476a9ab059c62b92)
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