xref: /freebsd/sys/dev/pcf/pcf.c (revision 718cf2ccb9956613756ab15d7a0e28f2c8e91cab)
14e190a62SNicolas Souchu /*-
2*718cf2ccSPedro F. Giffuni  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3*718cf2ccSPedro 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--) {
694e190a62SNicolas Souchu 
70e0efc557SJoerg Wunsch 		if ((pcf_get_S1(sc) & PIN) == 0)
714e190a62SNicolas Souchu 			return (0);
724e190a62SNicolas Souchu 	}
734e190a62SNicolas Souchu 
744f16b8b1SNicolas Souchu #ifdef PCFDEBUG
754f16b8b1SNicolas Souchu 	printf("pcf: timeout!\n");
764f16b8b1SNicolas Souchu #endif
774f16b8b1SNicolas Souchu 
784e190a62SNicolas Souchu 	return (IIC_ETIMEOUT);
794e190a62SNicolas Souchu }
804e190a62SNicolas Souchu 
8113e3657bSJohn Baldwin static void
8213e3657bSJohn Baldwin pcf_stop_locked(struct pcf_softc *sc)
834e190a62SNicolas Souchu {
844e190a62SNicolas Souchu 
8513e3657bSJohn Baldwin 	PCF_ASSERT_LOCKED(sc);
86e0efc557SJoerg Wunsch #ifdef PCFDEBUG
87e0efc557SJoerg Wunsch 	device_printf(dev, " >> stop\n");
88e0efc557SJoerg Wunsch #endif
89af548787SNicolas Souchu 	/*
90af548787SNicolas Souchu 	 * Send STOP condition iff the START condition was previously sent.
919d5abbddSJens Schweikhardt 	 * STOP is sent only once even if an iicbus_stop() is called after
92e0efc557SJoerg Wunsch 	 * an iicbus_read()... see pcf_read(): the PCF needs to send the stop
93af548787SNicolas Souchu 	 * before the last char is read.
94af548787SNicolas Souchu 	 */
95e0efc557SJoerg Wunsch 	if (sc->pcf_started) {
964e190a62SNicolas Souchu 		/* set stop condition and enable IT */
97e0efc557SJoerg Wunsch 		pcf_set_S1(sc, PIN|ESO|ENI|STO|ACK);
984e190a62SNicolas Souchu 
99e0efc557SJoerg Wunsch 		sc->pcf_started = 0;
100af548787SNicolas Souchu 	}
1014e190a62SNicolas Souchu }
1024e190a62SNicolas Souchu 
10313e3657bSJohn Baldwin static int
104e0efc557SJoerg Wunsch pcf_noack(struct pcf_softc *sc, int timeout)
105af548787SNicolas Souchu {
106af548787SNicolas Souchu 	int noack;
107af548787SNicolas Souchu 	int k = timeout/10;
108af548787SNicolas Souchu 
10913e3657bSJohn Baldwin 	PCF_ASSERT_LOCKED(sc);
110af548787SNicolas Souchu 	do {
111e0efc557SJoerg Wunsch 		noack = pcf_get_S1(sc) & LRB;
112af548787SNicolas Souchu 		if (!noack)
113af548787SNicolas Souchu 			break;
114af548787SNicolas Souchu 		DELAY(10);				/* XXX wait 10 us */
115af548787SNicolas Souchu 	} while (k--);
116af548787SNicolas Souchu 
117af548787SNicolas Souchu 	return (noack);
118af548787SNicolas Souchu }
119af548787SNicolas Souchu 
120e0efc557SJoerg Wunsch int
121e0efc557SJoerg Wunsch pcf_repeated_start(device_t dev, u_char slave, int timeout)
1224e190a62SNicolas Souchu {
123e0efc557SJoerg Wunsch 	struct pcf_softc *sc = DEVTOSOFTC(dev);
1244e190a62SNicolas Souchu 	int error = 0;
1254e190a62SNicolas Souchu 
12613e3657bSJohn Baldwin 	PCF_LOCK(sc);
127e0efc557SJoerg Wunsch #ifdef PCFDEBUG
128e0efc557SJoerg Wunsch 	device_printf(dev, " >> repeated start for slave %#x\n",
129e0efc557SJoerg Wunsch 		      (unsigned)slave);
130e0efc557SJoerg Wunsch #endif
1314e190a62SNicolas Souchu 	/* repeated start */
132e0efc557SJoerg Wunsch 	pcf_set_S1(sc, ESO|STA|STO|ACK);
1334e190a62SNicolas Souchu 
1344e190a62SNicolas Souchu 	/* set slave address to PCF. Last bit (LSB) must be set correctly
1354e190a62SNicolas Souchu 	 * according to transfer direction */
136e0efc557SJoerg Wunsch 	pcf_set_S0(sc, slave);
1374e190a62SNicolas Souchu 
1384e190a62SNicolas Souchu 	/* wait for address sent, polling */
139e0efc557SJoerg Wunsch 	if ((error = pcf_wait_byte(sc)))
1404e190a62SNicolas Souchu 		goto error;
1414e190a62SNicolas Souchu 
142af548787SNicolas Souchu 	/* check for ack */
143e0efc557SJoerg Wunsch 	if (pcf_noack(sc, timeout)) {
1444e190a62SNicolas Souchu 		error = IIC_ENOACK;
1454f16b8b1SNicolas Souchu #ifdef PCFDEBUG
1464f16b8b1SNicolas Souchu 		printf("pcf: no ack on repeated_start!\n");
1474f16b8b1SNicolas Souchu #endif
1484e190a62SNicolas Souchu 		goto error;
1494e190a62SNicolas Souchu 	}
1504e190a62SNicolas Souchu 
15113e3657bSJohn Baldwin 	PCF_UNLOCK(sc);
1524e190a62SNicolas Souchu 	return (0);
1534e190a62SNicolas Souchu 
1544e190a62SNicolas Souchu error:
15513e3657bSJohn Baldwin 	pcf_stop_locked(sc);
15613e3657bSJohn Baldwin 	PCF_UNLOCK(sc);
1574e190a62SNicolas Souchu 	return (error);
1584e190a62SNicolas Souchu }
1594e190a62SNicolas Souchu 
160e0efc557SJoerg Wunsch int
161e0efc557SJoerg Wunsch pcf_start(device_t dev, u_char slave, int timeout)
1624e190a62SNicolas Souchu {
163e0efc557SJoerg Wunsch 	struct pcf_softc *sc = DEVTOSOFTC(dev);
1644e190a62SNicolas Souchu 	int error = 0;
1654e190a62SNicolas Souchu 
16613e3657bSJohn Baldwin 	PCF_LOCK(sc);
167e0efc557SJoerg Wunsch #ifdef PCFDEBUG
168e0efc557SJoerg Wunsch 	device_printf(dev, " >> start for slave %#x\n", (unsigned)slave);
169e0efc557SJoerg Wunsch #endif
1704f16b8b1SNicolas Souchu 	if ((pcf_get_S1(sc) & nBB) == 0) {
1714f16b8b1SNicolas Souchu #ifdef PCFDEBUG
1724f16b8b1SNicolas Souchu 		printf("pcf: busy!\n");
1734f16b8b1SNicolas Souchu #endif
17413e3657bSJohn Baldwin 		PCF_UNLOCK(sc);
175d1e99670SIan Lepore 		return (IIC_EBUSERR);
1764f16b8b1SNicolas Souchu 	}
1774e190a62SNicolas Souchu 
1784e190a62SNicolas Souchu 	/* set slave address to PCF. Last bit (LSB) must be set correctly
1794e190a62SNicolas Souchu 	 * according to transfer direction */
180e0efc557SJoerg Wunsch 	pcf_set_S0(sc, slave);
1814e190a62SNicolas Souchu 
1824e190a62SNicolas Souchu 	/* START only */
183e0efc557SJoerg Wunsch 	pcf_set_S1(sc, PIN|ESO|STA|ACK);
1844e190a62SNicolas Souchu 
185e0efc557SJoerg Wunsch 	sc->pcf_started = 1;
186af548787SNicolas Souchu 
1874e190a62SNicolas Souchu 	/* wait for address sent, polling */
188e0efc557SJoerg Wunsch 	if ((error = pcf_wait_byte(sc)))
1894e190a62SNicolas Souchu 		goto error;
1904e190a62SNicolas Souchu 
191af548787SNicolas Souchu 	/* check for ACK */
192e0efc557SJoerg Wunsch 	if (pcf_noack(sc, timeout)) {
1934e190a62SNicolas Souchu 		error = IIC_ENOACK;
1944f16b8b1SNicolas Souchu #ifdef PCFDEBUG
1954f16b8b1SNicolas Souchu 		printf("pcf: no ack on start!\n");
1964f16b8b1SNicolas Souchu #endif
1974e190a62SNicolas Souchu 		goto error;
1984e190a62SNicolas Souchu 	}
1994e190a62SNicolas Souchu 
20013e3657bSJohn Baldwin 	PCF_UNLOCK(sc);
2014e190a62SNicolas Souchu 	return (0);
2024e190a62SNicolas Souchu 
2034e190a62SNicolas Souchu error:
20413e3657bSJohn Baldwin 	pcf_stop_locked(sc);
20513e3657bSJohn Baldwin 	PCF_UNLOCK(sc);
2064e190a62SNicolas Souchu 	return (error);
2074e190a62SNicolas Souchu }
2084e190a62SNicolas Souchu 
20913e3657bSJohn Baldwin int
21013e3657bSJohn Baldwin pcf_stop(device_t dev)
21113e3657bSJohn Baldwin {
21213e3657bSJohn Baldwin 	struct pcf_softc *sc = DEVTOSOFTC(dev);
21313e3657bSJohn Baldwin 
21413e3657bSJohn Baldwin #ifdef PCFDEBUG
21513e3657bSJohn Baldwin 	device_printf(dev, " >> stop\n");
21613e3657bSJohn Baldwin #endif
21713e3657bSJohn Baldwin 	PCF_LOCK(sc);
21813e3657bSJohn Baldwin 	pcf_stop_locked(sc);
21913e3657bSJohn Baldwin 	PCF_UNLOCK(sc);
22013e3657bSJohn Baldwin 
22113e3657bSJohn Baldwin 	return (0);
22213e3657bSJohn Baldwin }
22313e3657bSJohn Baldwin 
224e0efc557SJoerg Wunsch void
225e0efc557SJoerg Wunsch pcf_intr(void *arg)
2264e190a62SNicolas Souchu {
2274f16b8b1SNicolas Souchu 	struct pcf_softc *sc = arg;
2284e190a62SNicolas Souchu 	char data, status, addr;
2294e190a62SNicolas Souchu 	char error = 0;
2304e190a62SNicolas Souchu 
23113e3657bSJohn Baldwin 	PCF_LOCK(sc);
232e0efc557SJoerg Wunsch 	status = pcf_get_S1(sc);
2334e190a62SNicolas Souchu 
2344e190a62SNicolas Souchu 	if (status & PIN) {
2354f16b8b1SNicolas Souchu 		printf("pcf: spurious interrupt, status=0x%x\n",
236e0efc557SJoerg Wunsch 		       status & 0xff);
2374e190a62SNicolas Souchu 
2384e190a62SNicolas Souchu 		goto error;
2394e190a62SNicolas Souchu 	}
2404e190a62SNicolas Souchu 
2414e190a62SNicolas Souchu 	if (status & LAB)
2424f16b8b1SNicolas Souchu 		printf("pcf: bus arbitration lost!\n");
2434e190a62SNicolas Souchu 
2444e190a62SNicolas Souchu 	if (status & BER) {
2454e190a62SNicolas Souchu 		error = IIC_EBUSERR;
246e0efc557SJoerg Wunsch 		iicbus_intr(sc->iicbus, INTR_ERROR, &error);
2474e190a62SNicolas Souchu 
2484e190a62SNicolas Souchu 		goto error;
2494e190a62SNicolas Souchu 	}
2504e190a62SNicolas Souchu 
2514e190a62SNicolas Souchu 	do {
252e0efc557SJoerg Wunsch 		status = pcf_get_S1(sc);
2534e190a62SNicolas Souchu 
254e0efc557SJoerg Wunsch 		switch(sc->pcf_slave_mode) {
2554e190a62SNicolas Souchu 
2564e190a62SNicolas Souchu 		case SLAVE_TRANSMITTER:
2574e190a62SNicolas Souchu 			if (status & LRB) {
2584e190a62SNicolas Souchu 				/* ack interrupt line */
259e0efc557SJoerg Wunsch 				dummy_write(sc);
2604e190a62SNicolas Souchu 
2614e190a62SNicolas Souchu 				/* no ack, don't send anymore */
262e0efc557SJoerg Wunsch 				sc->pcf_slave_mode = SLAVE_RECEIVER;
2634e190a62SNicolas Souchu 
264e0efc557SJoerg Wunsch 				iicbus_intr(sc->iicbus, INTR_NOACK, NULL);
2654e190a62SNicolas Souchu 				break;
2664e190a62SNicolas Souchu 			}
2674e190a62SNicolas Souchu 
2684e190a62SNicolas Souchu 			/* get data from upper code */
269e0efc557SJoerg Wunsch 			iicbus_intr(sc->iicbus, INTR_TRANSMIT, &data);
2704e190a62SNicolas Souchu 
271e0efc557SJoerg Wunsch 			pcf_set_S0(sc, data);
2724e190a62SNicolas Souchu 			break;
2734e190a62SNicolas Souchu 
2744e190a62SNicolas Souchu 		case SLAVE_RECEIVER:
2754e190a62SNicolas Souchu 			if (status & AAS) {
276e0efc557SJoerg Wunsch 				addr = pcf_get_S0(sc);
2774e190a62SNicolas Souchu 
2784e190a62SNicolas Souchu 				if (status & AD0)
279e0efc557SJoerg Wunsch 					iicbus_intr(sc->iicbus, INTR_GENERAL, &addr);
2804e190a62SNicolas Souchu 				else
281e0efc557SJoerg Wunsch 					iicbus_intr(sc->iicbus, INTR_START, &addr);
2824e190a62SNicolas Souchu 
2834e190a62SNicolas Souchu 				if (addr & LSB) {
284e0efc557SJoerg Wunsch 					sc->pcf_slave_mode = SLAVE_TRANSMITTER;
2854e190a62SNicolas Souchu 
2864e190a62SNicolas Souchu 					/* get the first char from upper code */
287e0efc557SJoerg Wunsch 					iicbus_intr(sc->iicbus, INTR_TRANSMIT, &data);
2884e190a62SNicolas Souchu 
2894e190a62SNicolas Souchu 					/* send first data byte */
290e0efc557SJoerg Wunsch 					pcf_set_S0(sc, data);
2914e190a62SNicolas Souchu 				}
2924e190a62SNicolas Souchu 
2934e190a62SNicolas Souchu 				break;
2944e190a62SNicolas Souchu 			}
2954e190a62SNicolas Souchu 
2964e190a62SNicolas Souchu 			/* stop condition received? */
2974e190a62SNicolas Souchu 			if (status & STS) {
2984e190a62SNicolas Souchu 				/* ack interrupt line */
299e0efc557SJoerg Wunsch 				dummy_read(sc);
3004e190a62SNicolas Souchu 
3014e190a62SNicolas Souchu 				/* emulate intr stop condition */
302e0efc557SJoerg Wunsch 				iicbus_intr(sc->iicbus, INTR_STOP, NULL);
3034e190a62SNicolas Souchu 
3044e190a62SNicolas Souchu 			} else {
3054e190a62SNicolas Souchu 				/* get data, ack interrupt line */
306e0efc557SJoerg Wunsch 				data = pcf_get_S0(sc);
3074e190a62SNicolas Souchu 
3084e190a62SNicolas Souchu 				/* deliver the character */
309e0efc557SJoerg Wunsch 				iicbus_intr(sc->iicbus, INTR_RECEIVE, &data);
3104e190a62SNicolas Souchu 			}
3114e190a62SNicolas Souchu 			break;
3124e190a62SNicolas Souchu 
3134e190a62SNicolas Souchu 		    default:
3146e551fb6SDavid E. O'Brien 			panic("%s: unknown slave mode (%d)!", __func__,
315e0efc557SJoerg Wunsch 				sc->pcf_slave_mode);
3164e190a62SNicolas Souchu 		    }
3174e190a62SNicolas Souchu 
318e0efc557SJoerg Wunsch 	} while ((pcf_get_S1(sc) & PIN) == 0);
31913e3657bSJohn Baldwin 	PCF_UNLOCK(sc);
3204e190a62SNicolas Souchu 
3214e190a62SNicolas Souchu 	return;
3224e190a62SNicolas Souchu 
3234e190a62SNicolas Souchu error:
3244e190a62SNicolas Souchu 	/* unknown event on bus...reset PCF */
325e0efc557SJoerg Wunsch 	pcf_set_S1(sc, PIN|ESO|ENI|ACK);
3264e190a62SNicolas Souchu 
327e0efc557SJoerg Wunsch 	sc->pcf_slave_mode = SLAVE_RECEIVER;
32813e3657bSJohn Baldwin 	PCF_UNLOCK(sc);
3294e190a62SNicolas Souchu 
3304e190a62SNicolas Souchu 	return;
3314e190a62SNicolas Souchu }
3324e190a62SNicolas Souchu 
333e0efc557SJoerg Wunsch int
334e0efc557SJoerg Wunsch pcf_rst_card(device_t dev, u_char speed, u_char addr, u_char *oldaddr)
3354e190a62SNicolas Souchu {
336e0efc557SJoerg Wunsch 	struct pcf_softc *sc = DEVTOSOFTC(dev);
337af548787SNicolas Souchu 
33813e3657bSJohn Baldwin 	PCF_LOCK(sc);
339af548787SNicolas Souchu 	if (oldaddr)
340e0efc557SJoerg Wunsch 		*oldaddr = sc->pcf_addr;
3414e190a62SNicolas Souchu 
3424e190a62SNicolas Souchu 	/* retrieve own address from bus level */
343af548787SNicolas Souchu 	if (!addr)
344e0efc557SJoerg Wunsch 		sc->pcf_addr = PCF_DEFAULT_ADDR;
345af548787SNicolas Souchu 	else
346e0efc557SJoerg Wunsch 		sc->pcf_addr = addr;
3474e190a62SNicolas Souchu 
348e0efc557SJoerg Wunsch 	pcf_set_S1(sc, PIN);				/* initialize S1 */
3494e190a62SNicolas Souchu 
3504e190a62SNicolas Souchu 	/* own address S'O<>0 */
351e0efc557SJoerg Wunsch 	pcf_set_S0(sc, sc->pcf_addr >> 1);
3524e190a62SNicolas Souchu 
3534e190a62SNicolas Souchu 	/* select clock register */
354e0efc557SJoerg Wunsch 	pcf_set_S1(sc, PIN|ES1);
3554e190a62SNicolas Souchu 
3564e190a62SNicolas Souchu 	/* select bus speed : 18=90kb, 19=45kb, 1A=11kb, 1B=1.5kb */
3574e190a62SNicolas Souchu 	switch (speed) {
3584e190a62SNicolas Souchu 	case IIC_SLOW:
359e0efc557SJoerg Wunsch 		pcf_set_S0(sc,  0x1b); /* XXX Sun uses 0x1f */
3604e190a62SNicolas Souchu 		break;
3614e190a62SNicolas Souchu 
3624e190a62SNicolas Souchu 	case IIC_FAST:
363e0efc557SJoerg Wunsch 		pcf_set_S0(sc,  0x19); /* XXX Sun: 0x1d */
3644e190a62SNicolas Souchu 		break;
3654e190a62SNicolas Souchu 
3664e190a62SNicolas Souchu 	case IIC_UNKNOWN:
3674e190a62SNicolas Souchu 	case IIC_FASTEST:
3684e190a62SNicolas Souchu 	default:
369e0efc557SJoerg Wunsch 		pcf_set_S0(sc,  0x18); /* XXX Sun: 0x1c */
3704e190a62SNicolas Souchu 		break;
3714e190a62SNicolas Souchu 	}
3724e190a62SNicolas Souchu 
3734e190a62SNicolas Souchu 	/* set bus on, ack=yes, INT=yes */
374e0efc557SJoerg Wunsch 	pcf_set_S1(sc, PIN|ESO|ENI|ACK);
3754e190a62SNicolas Souchu 
376e0efc557SJoerg Wunsch 	sc->pcf_slave_mode = SLAVE_RECEIVER;
37713e3657bSJohn Baldwin 	PCF_UNLOCK(sc);
3784e190a62SNicolas Souchu 
3794e190a62SNicolas Souchu 	return (0);
3804e190a62SNicolas Souchu }
3814e190a62SNicolas Souchu 
382e0efc557SJoerg Wunsch int
3831c44eb75SAndriy Gapon pcf_write(device_t dev, const char *buf, int len, int *sent, int timeout /* us */)
3844e190a62SNicolas Souchu {
385e0efc557SJoerg Wunsch 	struct pcf_softc *sc = DEVTOSOFTC(dev);
3864e190a62SNicolas Souchu 	int bytes, error = 0;
3874e190a62SNicolas Souchu 
3884e190a62SNicolas Souchu #ifdef PCFDEBUG
389e0efc557SJoerg Wunsch 	device_printf(dev, " >> writing %d bytes: %#x%s\n", len,
390e0efc557SJoerg Wunsch 		      (unsigned)buf[0], len > 1? "...": "");
3914e190a62SNicolas Souchu #endif
3924e190a62SNicolas Souchu 
3934e190a62SNicolas Souchu 	bytes = 0;
39413e3657bSJohn Baldwin 	PCF_LOCK(sc);
3954e190a62SNicolas Souchu 	while (len) {
3964e190a62SNicolas Souchu 
397e0efc557SJoerg Wunsch 		pcf_set_S0(sc, *buf++);
3984e190a62SNicolas Souchu 
399af548787SNicolas Souchu 		/* wait for the byte to be send */
400e0efc557SJoerg Wunsch 		if ((error = pcf_wait_byte(sc)))
4014e190a62SNicolas Souchu 			goto error;
4024e190a62SNicolas Souchu 
403af548787SNicolas Souchu 		/* check if ack received */
404e0efc557SJoerg Wunsch 		if (pcf_noack(sc, timeout)) {
4054e190a62SNicolas Souchu 			error = IIC_ENOACK;
4064e190a62SNicolas Souchu 			goto error;
4074e190a62SNicolas Souchu 		}
4084e190a62SNicolas Souchu 
4094e190a62SNicolas Souchu 		len --;
4104e190a62SNicolas Souchu 		bytes ++;
4114e190a62SNicolas Souchu 	}
4124e190a62SNicolas Souchu 
4134e190a62SNicolas Souchu error:
4144e190a62SNicolas Souchu 	*sent = bytes;
41513e3657bSJohn Baldwin 	PCF_UNLOCK(sc);
4164e190a62SNicolas Souchu 
4174e190a62SNicolas Souchu #ifdef PCFDEBUG
418e0efc557SJoerg Wunsch 	device_printf(dev, " >> %d bytes written (%d)\n", bytes, error);
4194e190a62SNicolas Souchu #endif
4204e190a62SNicolas Souchu 
4214e190a62SNicolas Souchu 	return (error);
4224e190a62SNicolas Souchu }
4234e190a62SNicolas Souchu 
424e0efc557SJoerg Wunsch int
425e0efc557SJoerg Wunsch pcf_read(device_t dev, char *buf, int len, int *read, int last,
426af548787SNicolas Souchu 	 int delay /* us */)
4274e190a62SNicolas Souchu {
428e0efc557SJoerg Wunsch 	struct pcf_softc *sc = DEVTOSOFTC(dev);
4294e190a62SNicolas Souchu 	int bytes, error = 0;
4304e190a62SNicolas Souchu #ifdef PCFDEBUG
431e0efc557SJoerg Wunsch 	char *obuf = buf;
432e0efc557SJoerg Wunsch 
433e0efc557SJoerg Wunsch 	device_printf(dev, " << reading %d bytes\n", len);
4344e190a62SNicolas Souchu #endif
4354e190a62SNicolas Souchu 
43613e3657bSJohn Baldwin 	PCF_LOCK(sc);
4374e190a62SNicolas Souchu 	/* trig the bus to get the first data byte in S0 */
4384e190a62SNicolas Souchu 	if (len) {
439af548787SNicolas Souchu 		if (len == 1 && last)
4404e190a62SNicolas Souchu 			/* just one byte to read */
441e0efc557SJoerg Wunsch 			pcf_set_S1(sc, ESO);		/* no ack */
4424e190a62SNicolas Souchu 
443e0efc557SJoerg Wunsch 		dummy_read(sc);
4444e190a62SNicolas Souchu 	}
4454e190a62SNicolas Souchu 
4464e190a62SNicolas Souchu 	bytes = 0;
4474e190a62SNicolas Souchu 	while (len) {
4484e190a62SNicolas Souchu 
449af548787SNicolas Souchu 		/* XXX delay needed here */
450af548787SNicolas Souchu 
451af548787SNicolas Souchu 		/* wait for trigged byte */
452e0efc557SJoerg Wunsch 		if ((error = pcf_wait_byte(sc))) {
45313e3657bSJohn Baldwin 			pcf_stop_locked(sc);
4544e190a62SNicolas Souchu 			goto error;
4554e190a62SNicolas Souchu 		}
4564e190a62SNicolas Souchu 
457af548787SNicolas Souchu 		if (len == 1 && last)
458af548787SNicolas Souchu 			/* ok, last data byte already in S0, no I2C activity
459e0efc557SJoerg Wunsch 			 * on next pcf_get_S0() */
46013e3657bSJohn Baldwin 			pcf_stop_locked(sc);
4614e190a62SNicolas Souchu 
462af548787SNicolas Souchu 		else if (len == 2 && last)
4634e190a62SNicolas Souchu 			/* next trigged byte with no ack */
464e0efc557SJoerg Wunsch 			pcf_set_S1(sc, ESO);
4654e190a62SNicolas Souchu 
466af548787SNicolas Souchu 		/* receive byte, trig next byte */
467e0efc557SJoerg Wunsch 		*buf++ = pcf_get_S0(sc);
4684e190a62SNicolas Souchu 
4694e190a62SNicolas Souchu 		len --;
4704e190a62SNicolas Souchu 		bytes ++;
47174b8d63dSPedro F. Giffuni 	}
4724e190a62SNicolas Souchu 
4734e190a62SNicolas Souchu error:
4744e190a62SNicolas Souchu 	*read = bytes;
47513e3657bSJohn Baldwin 	PCF_UNLOCK(sc);
4764e190a62SNicolas Souchu 
4774e190a62SNicolas Souchu #ifdef PCFDEBUG
478e0efc557SJoerg Wunsch 	device_printf(dev, " << %d bytes read (%d): %#x%s\n", bytes, error,
479e0efc557SJoerg Wunsch 		      (unsigned)obuf[0], bytes > 1? "...": "");
4804e190a62SNicolas Souchu #endif
4814e190a62SNicolas Souchu 
4824e190a62SNicolas Souchu 	return (error);
4834e190a62SNicolas Souchu }
4849e58d59fSJohn Baldwin 
4859e58d59fSJohn Baldwin DRIVER_MODULE(iicbus, pcf, iicbus_driver, iicbus_devclass, 0, 0);
4869e58d59fSJohn Baldwin MODULE_DEPEND(pcf, iicbus, PCF_MINVER, PCF_PREFVER, PCF_MAXVER);
4879e58d59fSJohn Baldwin MODULE_VERSION(pcf, PCF_MODVER);
488