xref: /freebsd/sys/dev/pcf/pcf.c (revision af5487872e13a303aacabc5ec31b4821f7b15c32)
14e190a62SNicolas Souchu /*-
24e190a62SNicolas Souchu  * Copyright (c) 1998 Nicolas Souchu, Marc Bouget
34e190a62SNicolas Souchu  * All rights reserved.
44e190a62SNicolas Souchu  *
54e190a62SNicolas Souchu  * Redistribution and use in source and binary forms, with or without
64e190a62SNicolas Souchu  * modification, are permitted provided that the following conditions
74e190a62SNicolas Souchu  * are met:
84e190a62SNicolas Souchu  * 1. Redistributions of source code must retain the above copyright
94e190a62SNicolas Souchu  *    notice, this list of conditions and the following disclaimer.
104e190a62SNicolas Souchu  * 2. Redistributions in binary form must reproduce the above copyright
114e190a62SNicolas Souchu  *    notice, this list of conditions and the following disclaimer in the
124e190a62SNicolas Souchu  *    documentation and/or other materials provided with the distribution.
134e190a62SNicolas Souchu  *
144e190a62SNicolas Souchu  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
154e190a62SNicolas Souchu  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
164e190a62SNicolas Souchu  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
174e190a62SNicolas Souchu  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
184e190a62SNicolas Souchu  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
194e190a62SNicolas Souchu  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
204e190a62SNicolas Souchu  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
214e190a62SNicolas Souchu  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
224e190a62SNicolas Souchu  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
234e190a62SNicolas Souchu  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
244e190a62SNicolas Souchu  * SUCH DAMAGE.
254e190a62SNicolas Souchu  *
26af548787SNicolas Souchu  *	$Id: pcf.c,v 1.2 1998/10/22 05:58:40 bde Exp $
274e190a62SNicolas Souchu  *
284e190a62SNicolas Souchu  */
294e190a62SNicolas Souchu #include <sys/param.h>
304e190a62SNicolas Souchu #include <sys/systm.h>
314e190a62SNicolas Souchu #include <sys/kernel.h>
324e190a62SNicolas Souchu #include <sys/module.h>
334e190a62SNicolas Souchu #include <sys/bus.h>
344e190a62SNicolas Souchu #include <sys/conf.h>
354e190a62SNicolas Souchu #include <sys/malloc.h>
364e190a62SNicolas Souchu 
374e190a62SNicolas Souchu #include <machine/clock.h>
384e190a62SNicolas Souchu 
394e190a62SNicolas Souchu #include <i386/isa/isa_device.h>
404e190a62SNicolas Souchu 
414e190a62SNicolas Souchu #include <dev/iicbus/iiconf.h>
424e190a62SNicolas Souchu #include "iicbus_if.h"
434e190a62SNicolas Souchu 
44af548787SNicolas Souchu #define TIMEOUT	9999					/* XXX */
454e190a62SNicolas Souchu 
464e190a62SNicolas Souchu /* Status bits of S1 register (read only) */
474e190a62SNicolas Souchu #define nBB	0x01		/* busy when low set/reset by STOP/START*/
484e190a62SNicolas Souchu #define LAB	0x02		/* lost arbitration bit in multi-master mode */
494e190a62SNicolas Souchu #define AAS	0x04		/* addressed as slave */
504e190a62SNicolas Souchu #define LRB	0x08		/* last received byte when not AAS */
514e190a62SNicolas Souchu #define AD0	0x08		/* general call received when AAS */
524e190a62SNicolas Souchu #define BER	0x10		/* bus error, misplaced START or STOP */
534e190a62SNicolas Souchu #define STS	0x20		/* STOP detected in slave receiver mode */
544e190a62SNicolas Souchu #define PIN	0x80		/* pending interrupt not (r/w) */
554e190a62SNicolas Souchu 
564e190a62SNicolas Souchu /* Control bits of S1 register (write only) */
574e190a62SNicolas Souchu #define ACK	0x01
584e190a62SNicolas Souchu #define STO	0x02
594e190a62SNicolas Souchu #define STA	0x04
604e190a62SNicolas Souchu #define ENI	0x08
614e190a62SNicolas Souchu #define ES2	0x10
624e190a62SNicolas Souchu #define ES1	0x20
634e190a62SNicolas Souchu #define ES0	0x40
644e190a62SNicolas Souchu 
654e190a62SNicolas Souchu #define BUFSIZE 2048
664e190a62SNicolas Souchu 
674e190a62SNicolas Souchu #define SLAVE_TRANSMITTER	0x1
684e190a62SNicolas Souchu #define SLAVE_RECEIVER		0x2
694e190a62SNicolas Souchu 
70af548787SNicolas Souchu #define PCF_DEFAULT_ADDR	0xaa
71af548787SNicolas Souchu 
724e190a62SNicolas Souchu struct pcf_softc {
734e190a62SNicolas Souchu 
744e190a62SNicolas Souchu 	int pcf_base;			/* isa port */
75af548787SNicolas Souchu 	u_char pcf_addr;		/* interface I2C address */
764e190a62SNicolas Souchu 
774e190a62SNicolas Souchu 	int pcf_slave_mode;		/* receiver or transmitter */
78af548787SNicolas Souchu 	int pcf_started;		/* 1 if start condition sent */
794e190a62SNicolas Souchu 
804e190a62SNicolas Souchu 	device_t iicbus;		/* the corresponding iicbus */
814e190a62SNicolas Souchu };
824e190a62SNicolas Souchu 
834e190a62SNicolas Souchu struct pcf_isa_softc {
844e190a62SNicolas Souchu 
854e190a62SNicolas Souchu 	int pcf_unit;			/* unit of the isa device */
864e190a62SNicolas Souchu 	int pcf_base;			/* isa port */
874e190a62SNicolas Souchu 	int pcf_irq;			/* isa irq or null if polled */
884e190a62SNicolas Souchu 
894e190a62SNicolas Souchu 	unsigned int pcf_flags;		/* boot flags */
904e190a62SNicolas Souchu };
914e190a62SNicolas Souchu 
924e190a62SNicolas Souchu #define MAXPCF 2
934e190a62SNicolas Souchu 
944e190a62SNicolas Souchu static struct pcf_isa_softc *pcfdata[MAXPCF];
954e190a62SNicolas Souchu static npcf = 0;
964e190a62SNicolas Souchu 
974e190a62SNicolas Souchu static int	pcfprobe_isa(struct isa_device *);
984e190a62SNicolas Souchu static int	pcfattach_isa(struct isa_device *);
994e190a62SNicolas Souchu 
1004e190a62SNicolas Souchu struct isa_driver pcfdriver = {
1014e190a62SNicolas Souchu 	pcfprobe_isa, pcfattach_isa, "pcf"
1024e190a62SNicolas Souchu };
1034e190a62SNicolas Souchu 
1044e190a62SNicolas Souchu static int pcf_probe(device_t);
1054e190a62SNicolas Souchu static int pcf_attach(device_t);
1064e190a62SNicolas Souchu static void pcf_print_child(device_t, device_t);
1074e190a62SNicolas Souchu 
108af548787SNicolas Souchu static int pcf_repeated_start(device_t, u_char, int);
109af548787SNicolas Souchu static int pcf_start(device_t, u_char, int);
1104e190a62SNicolas Souchu static int pcf_stop(device_t);
1114e190a62SNicolas Souchu static int pcf_write(device_t, char *, int, int *);
1124e190a62SNicolas Souchu static int pcf_read(device_t, char *, int, int *);
113fe310de8SBruce Evans static ointhand2_t pcfintr;
1144e190a62SNicolas Souchu static int pcf_rst_card(device_t, u_char);
1154e190a62SNicolas Souchu 
1164e190a62SNicolas Souchu static device_method_t pcf_methods[] = {
1174e190a62SNicolas Souchu 	/* device interface */
1184e190a62SNicolas Souchu 	DEVMETHOD(device_probe,		pcf_probe),
1194e190a62SNicolas Souchu 	DEVMETHOD(device_attach,	pcf_attach),
1204e190a62SNicolas Souchu 
1214e190a62SNicolas Souchu 	/* bus interface */
1224e190a62SNicolas Souchu 	DEVMETHOD(bus_print_child,	pcf_print_child),
1234e190a62SNicolas Souchu 
1244e190a62SNicolas Souchu 	/* iicbus interface */
125af548787SNicolas Souchu 	DEVMETHOD(iicbus_callback,	iicbus_null_callback),
1264e190a62SNicolas Souchu 	DEVMETHOD(iicbus_repeated_start, pcf_repeated_start),
1274e190a62SNicolas Souchu 	DEVMETHOD(iicbus_start,		pcf_start),
1284e190a62SNicolas Souchu 	DEVMETHOD(iicbus_stop,		pcf_stop),
1294e190a62SNicolas Souchu 	DEVMETHOD(iicbus_write,		pcf_write),
1304e190a62SNicolas Souchu 	DEVMETHOD(iicbus_read,		pcf_read),
1314e190a62SNicolas Souchu 	DEVMETHOD(iicbus_reset,		pcf_rst_card),
1324e190a62SNicolas Souchu 
1334e190a62SNicolas Souchu 	{ 0, 0 }
1344e190a62SNicolas Souchu };
1354e190a62SNicolas Souchu 
1364e190a62SNicolas Souchu static driver_t pcf_driver = {
1374e190a62SNicolas Souchu 	"pcf",
1384e190a62SNicolas Souchu 	pcf_methods,
1394e190a62SNicolas Souchu 	DRIVER_TYPE_MISC,
1404e190a62SNicolas Souchu 	sizeof(struct pcf_softc),
1414e190a62SNicolas Souchu };
1424e190a62SNicolas Souchu 
1434e190a62SNicolas Souchu static devclass_t pcf_devclass;
1444e190a62SNicolas Souchu 
1454e190a62SNicolas Souchu #define DEVTOSOFTC(dev) ((struct pcf_softc *)device_get_softc(dev))
1464e190a62SNicolas Souchu 
1474e190a62SNicolas Souchu static int
1484e190a62SNicolas Souchu pcfprobe_isa(struct isa_device *dvp)
1494e190a62SNicolas Souchu {
1504e190a62SNicolas Souchu 	device_t pcfdev;
1514e190a62SNicolas Souchu 	struct pcf_isa_softc *pcf;
1524e190a62SNicolas Souchu 	int error;
1534e190a62SNicolas Souchu 
1544e190a62SNicolas Souchu 	if (npcf >= MAXPCF)
1554e190a62SNicolas Souchu 		return (0);
1564e190a62SNicolas Souchu 
1574e190a62SNicolas Souchu 	if ((pcf = (struct pcf_isa_softc *)malloc(sizeof(struct pcf_isa_softc),
1584e190a62SNicolas Souchu 			M_DEVBUF, M_NOWAIT)) == NULL)
1594e190a62SNicolas Souchu 		return (0);
1604e190a62SNicolas Souchu 
1614e190a62SNicolas Souchu 	pcf->pcf_base = dvp->id_iobase;		/* XXX should be ivars */
1624e190a62SNicolas Souchu 	pcf->pcf_unit = dvp->id_unit;
1634e190a62SNicolas Souchu 
1644e190a62SNicolas Souchu 	if (!(dvp->id_flags & IIC_POLLED))
1654e190a62SNicolas Souchu 		pcf->pcf_irq = (dvp->id_irq);
1664e190a62SNicolas Souchu 
1674e190a62SNicolas Souchu 	pcfdata[npcf++] = pcf;
1684e190a62SNicolas Souchu 
1694e190a62SNicolas Souchu 	/* XXX add the pcf device to the root_bus until isa bus exists */
1704e190a62SNicolas Souchu 	pcfdev = device_add_child(root_bus, "pcf", pcf->pcf_unit, NULL);
171af548787SNicolas Souchu 	device_probe_and_attach(pcfdev);
1724e190a62SNicolas Souchu 
1734e190a62SNicolas Souchu 	if (!pcfdev)
1744e190a62SNicolas Souchu 		goto error;
1754e190a62SNicolas Souchu 
1764e190a62SNicolas Souchu end_probe:
1774e190a62SNicolas Souchu 	return (1);
1784e190a62SNicolas Souchu 
1794e190a62SNicolas Souchu error:
1804e190a62SNicolas Souchu 	free(pcf, M_DEVBUF);
1814e190a62SNicolas Souchu 	return (0);
1824e190a62SNicolas Souchu }
1834e190a62SNicolas Souchu 
1844e190a62SNicolas Souchu static int
1854e190a62SNicolas Souchu pcfattach_isa(struct isa_device *isdp)
1864e190a62SNicolas Souchu {
187fe310de8SBruce Evans 	isdp->id_ointr = pcfintr;
1884e190a62SNicolas Souchu 	return (1);				/* ok */
1894e190a62SNicolas Souchu }
1904e190a62SNicolas Souchu 
1914e190a62SNicolas Souchu static int
1924e190a62SNicolas Souchu pcf_probe(device_t pcfdev)
1934e190a62SNicolas Souchu {
1944e190a62SNicolas Souchu 	struct pcf_softc *pcf = (struct pcf_softc *)device_get_softc(pcfdev);
195af548787SNicolas Souchu 	int unit = device_get_unit(pcfdev);
196af548787SNicolas Souchu 
197af548787SNicolas Souchu 	/* retrieve base address from isa initialization
198af548787SNicolas Souchu 	 *
199af548787SNicolas Souchu 	 * XXX should use ivars with isabus
200af548787SNicolas Souchu 	 */
201af548787SNicolas Souchu 	pcf->pcf_base = pcfdata[unit]->pcf_base;
202af548787SNicolas Souchu 
203af548787SNicolas Souchu 	/* reset the chip */
204af548787SNicolas Souchu 	pcf_rst_card(pcfdev, IIC_FASTEST, PCF_DEFAULT_ADDR, NULL);
2054e190a62SNicolas Souchu 
2064e190a62SNicolas Souchu 	/* XXX try do detect chipset */
2074e190a62SNicolas Souchu 
2084e190a62SNicolas Souchu 	device_set_desc(pcfdev, "PCF8584 I2C bus controller");
2094e190a62SNicolas Souchu 
2104e190a62SNicolas Souchu 	return (0);
2114e190a62SNicolas Souchu }
2124e190a62SNicolas Souchu 
2134e190a62SNicolas Souchu static int
2144e190a62SNicolas Souchu pcf_attach(device_t pcfdev)
2154e190a62SNicolas Souchu {
2164e190a62SNicolas Souchu 	struct pcf_softc *pcf = (struct pcf_softc *)device_get_softc(pcfdev);
2174e190a62SNicolas Souchu 
218af548787SNicolas Souchu 	pcf->iicbus = iicbus_alloc_bus(pcfdev);
219af548787SNicolas Souchu 
220af548787SNicolas Souchu 	if (!pcf->iicbus)
221af548787SNicolas Souchu 		return (EINVAL);
2224e190a62SNicolas Souchu 
2234e190a62SNicolas Souchu 	/* probe and attach the iicbus */
2244e190a62SNicolas Souchu 	device_probe_and_attach(pcf->iicbus);
2254e190a62SNicolas Souchu 
2264e190a62SNicolas Souchu 	return (0);
2274e190a62SNicolas Souchu }
2284e190a62SNicolas Souchu 
2294e190a62SNicolas Souchu static void
2304e190a62SNicolas Souchu pcf_print_child(device_t bus, device_t dev)
2314e190a62SNicolas Souchu {
232af548787SNicolas Souchu 	struct pcf_softc *pcf = (struct pcf_softc *)device_get_softc(bus);
233af548787SNicolas Souchu 
2344e190a62SNicolas Souchu 	printf(" on %s%d addr 0x%x", device_get_name(bus),
235af548787SNicolas Souchu 		device_get_unit(bus), (int)pcf->pcf_addr);
2364e190a62SNicolas Souchu 
2374e190a62SNicolas Souchu 	return;
2384e190a62SNicolas Souchu }
2394e190a62SNicolas Souchu 
2404e190a62SNicolas Souchu /*
2414e190a62SNicolas Souchu  * PCF8584 datasheet : when operate at 8 MHz or more, a minimun time of
2424e190a62SNicolas Souchu  * 6 clocks cycles must be left between two consecutives access
2434e190a62SNicolas Souchu  */
2444e190a62SNicolas Souchu #define pcf_nops()	DELAY(10)
2454e190a62SNicolas Souchu 
2464e190a62SNicolas Souchu #define dummy_read(pcf)		PCF_GET_S0(pcf)
2474e190a62SNicolas Souchu #define dummy_write(pcf)	PCF_SET_S0(pcf, 0)
2484e190a62SNicolas Souchu 
2494e190a62SNicolas Souchu /*
2504e190a62SNicolas Souchu  * Specific register access to PCF8584
2514e190a62SNicolas Souchu  */
2524e190a62SNicolas Souchu static void PCF_SET_S0(struct pcf_softc *pcf, int data)
2534e190a62SNicolas Souchu {
2544e190a62SNicolas Souchu 	outb(pcf->pcf_base, data);
2554e190a62SNicolas Souchu 	pcf_nops();
2564e190a62SNicolas Souchu }
2574e190a62SNicolas Souchu 
2584e190a62SNicolas Souchu static void PCF_SET_S1(struct pcf_softc *pcf, int data)
2594e190a62SNicolas Souchu {
2604e190a62SNicolas Souchu 	outb(pcf->pcf_base+1, data);
2614e190a62SNicolas Souchu 	pcf_nops();
2624e190a62SNicolas Souchu }
2634e190a62SNicolas Souchu 
2644e190a62SNicolas Souchu static char PCF_GET_S0(struct pcf_softc *pcf)
2654e190a62SNicolas Souchu {
2664e190a62SNicolas Souchu 	char data;
2674e190a62SNicolas Souchu 
2684e190a62SNicolas Souchu 	data = inb(pcf->pcf_base);
2694e190a62SNicolas Souchu 	pcf_nops();
2704e190a62SNicolas Souchu 
2714e190a62SNicolas Souchu 	return (data);
2724e190a62SNicolas Souchu }
2734e190a62SNicolas Souchu 
2744e190a62SNicolas Souchu static char PCF_GET_S1(struct pcf_softc *pcf)
2754e190a62SNicolas Souchu {
2764e190a62SNicolas Souchu 	char data;
2774e190a62SNicolas Souchu 
2784e190a62SNicolas Souchu 	data = inb(pcf->pcf_base+1);
2794e190a62SNicolas Souchu 	pcf_nops();
2804e190a62SNicolas Souchu 
2814e190a62SNicolas Souchu 	return (data);
2824e190a62SNicolas Souchu }
2834e190a62SNicolas Souchu 
2844e190a62SNicolas Souchu /*
2854e190a62SNicolas Souchu  * Polling mode for master operations wait for a new
2864e190a62SNicolas Souchu  * byte incomming or outgoing
2874e190a62SNicolas Souchu  */
2884e190a62SNicolas Souchu static int pcf_wait_byte(struct pcf_softc *pcf)
2894e190a62SNicolas Souchu {
2904e190a62SNicolas Souchu 	int counter = TIMEOUT;
2914e190a62SNicolas Souchu 
2924e190a62SNicolas Souchu 	while (counter--) {
2934e190a62SNicolas Souchu 
2944e190a62SNicolas Souchu 		if ((PCF_GET_S1(pcf) & PIN) == 0)
2954e190a62SNicolas Souchu 			return (0);
2964e190a62SNicolas Souchu 	}
2974e190a62SNicolas Souchu 
2984e190a62SNicolas Souchu 	return (IIC_ETIMEOUT);
2994e190a62SNicolas Souchu }
3004e190a62SNicolas Souchu 
3014e190a62SNicolas Souchu static int pcf_stop(device_t pcfdev)
3024e190a62SNicolas Souchu {
3034e190a62SNicolas Souchu 	struct pcf_softc *pcf = DEVTOSOFTC(pcfdev);
3044e190a62SNicolas Souchu 
305af548787SNicolas Souchu 	/*
306af548787SNicolas Souchu 	 * Send STOP condition iff the START condition was previously sent.
307af548787SNicolas Souchu 	 * STOP is sent only once even if a iicbus_stop() is called after
308af548787SNicolas Souchu 	 * an iicbus_read()... see pcf_read(): the pcf needs to send the stop
309af548787SNicolas Souchu 	 * before the last char is read.
310af548787SNicolas Souchu 	 */
311af548787SNicolas Souchu 	if (pcf->pcf_started) {
3124e190a62SNicolas Souchu 		/* set stop condition and enable IT */
3134e190a62SNicolas Souchu 		PCF_SET_S1(pcf, PIN|ES0|ENI|STO|ACK);
3144e190a62SNicolas Souchu 
315af548787SNicolas Souchu 		pcf->pcf_started = 0;
316af548787SNicolas Souchu 	}
317af548787SNicolas Souchu 
3184e190a62SNicolas Souchu 	return (0);
3194e190a62SNicolas Souchu }
3204e190a62SNicolas Souchu 
321af548787SNicolas Souchu 
322af548787SNicolas Souchu static int pcf_noack(struct pcf_softc *pcf, int timeout)
323af548787SNicolas Souchu {
324af548787SNicolas Souchu 	int noack;
325af548787SNicolas Souchu 	int k = timeout/10;
326af548787SNicolas Souchu 
327af548787SNicolas Souchu 	do {
328af548787SNicolas Souchu 		noack = PCF_GET_S1(pcf) & LRB;
329af548787SNicolas Souchu 		if (!noack)
330af548787SNicolas Souchu 			break;
331af548787SNicolas Souchu 		DELAY(10);				/* XXX wait 10 us */
332af548787SNicolas Souchu 	} while (k--);
333af548787SNicolas Souchu 
334af548787SNicolas Souchu 	return (noack);
335af548787SNicolas Souchu }
336af548787SNicolas Souchu 
337af548787SNicolas Souchu static int pcf_repeated_start(device_t pcfdev, u_char slave, int timeout)
3384e190a62SNicolas Souchu {
3394e190a62SNicolas Souchu 	struct pcf_softc *pcf = DEVTOSOFTC(pcfdev);
3404e190a62SNicolas Souchu 	int error = 0;
3414e190a62SNicolas Souchu 
3424e190a62SNicolas Souchu 	/* repeated start */
3434e190a62SNicolas Souchu 	PCF_SET_S1(pcf, ES0|STA|STO|ACK);
3444e190a62SNicolas Souchu 
3454e190a62SNicolas Souchu 	/* set slave address to PCF. Last bit (LSB) must be set correctly
3464e190a62SNicolas Souchu 	 * according to transfer direction */
3474e190a62SNicolas Souchu 	PCF_SET_S0(pcf, slave);
3484e190a62SNicolas Souchu 
3494e190a62SNicolas Souchu 	/* wait for address sent, polling */
3504e190a62SNicolas Souchu 	if ((error = pcf_wait_byte(pcf)))
3514e190a62SNicolas Souchu 		goto error;
3524e190a62SNicolas Souchu 
353af548787SNicolas Souchu 	/* check for ack */
354af548787SNicolas Souchu 	if (pcf_noack(pcf, timeout)) {
3554e190a62SNicolas Souchu 		error = IIC_ENOACK;
3564e190a62SNicolas Souchu 		goto error;
3574e190a62SNicolas Souchu 	}
3584e190a62SNicolas Souchu 
3594e190a62SNicolas Souchu 	return (0);
3604e190a62SNicolas Souchu 
3614e190a62SNicolas Souchu error:
3624e190a62SNicolas Souchu 	pcf_stop(pcfdev);
3634e190a62SNicolas Souchu 	return (error);
3644e190a62SNicolas Souchu }
3654e190a62SNicolas Souchu 
366af548787SNicolas Souchu static int pcf_start(device_t pcfdev, u_char slave, int timeout)
3674e190a62SNicolas Souchu {
3684e190a62SNicolas Souchu 	struct pcf_softc *pcf = DEVTOSOFTC(pcfdev);
3694e190a62SNicolas Souchu 	int error = 0;
3704e190a62SNicolas Souchu 
3714e190a62SNicolas Souchu 	if (PCF_GET_S1(pcf) & nBB == 0)
3724e190a62SNicolas Souchu 		return (IIC_EBUSBSY);
3734e190a62SNicolas Souchu 
3744e190a62SNicolas Souchu 	/* set slave address to PCF. Last bit (LSB) must be set correctly
3754e190a62SNicolas Souchu 	 * according to transfer direction */
3764e190a62SNicolas Souchu 	PCF_SET_S0(pcf, slave);
3774e190a62SNicolas Souchu 
3784e190a62SNicolas Souchu 	/* START only */
3794e190a62SNicolas Souchu 	PCF_SET_S1(pcf, PIN|ES0|STA|ACK);
3804e190a62SNicolas Souchu 
381af548787SNicolas Souchu 	pcf->pcf_started = 1;
382af548787SNicolas Souchu 
3834e190a62SNicolas Souchu 	/* wait for address sent, polling */
3844e190a62SNicolas Souchu 	if ((error = pcf_wait_byte(pcf)))
3854e190a62SNicolas Souchu 		goto error;
3864e190a62SNicolas Souchu 
387af548787SNicolas Souchu 	/* check for ACK */
388af548787SNicolas Souchu 	if (pcf_noack(pcf, timeout)) {
3894e190a62SNicolas Souchu 		error = IIC_ENOACK;
3904e190a62SNicolas Souchu 		goto error;
3914e190a62SNicolas Souchu 	}
3924e190a62SNicolas Souchu 
3934e190a62SNicolas Souchu 	return (0);
3944e190a62SNicolas Souchu 
3954e190a62SNicolas Souchu error:
3964e190a62SNicolas Souchu 	pcf_stop(pcfdev);
3974e190a62SNicolas Souchu 	return (error);
3984e190a62SNicolas Souchu }
3994e190a62SNicolas Souchu 
400fe310de8SBruce Evans static void
4014e190a62SNicolas Souchu pcfintr(unit)
4024e190a62SNicolas Souchu {
4034e190a62SNicolas Souchu 	struct pcf_softc *pcf =
4044e190a62SNicolas Souchu 		(struct pcf_softc *)devclass_get_softc(pcf_devclass, unit);
4054e190a62SNicolas Souchu 
4064e190a62SNicolas Souchu 	char data, status, addr;
4074e190a62SNicolas Souchu 	char error = 0;
4084e190a62SNicolas Souchu 
4094e190a62SNicolas Souchu 	status = PCF_GET_S1(pcf);
4104e190a62SNicolas Souchu 
4114e190a62SNicolas Souchu 	if (status & PIN) {
4124e190a62SNicolas Souchu 		printf("pcf%d: spurious interrupt, status=0x%x\n", unit,
4134e190a62SNicolas Souchu 			status & 0xff);
4144e190a62SNicolas Souchu 
4154e190a62SNicolas Souchu 		goto error;
4164e190a62SNicolas Souchu 	}
4174e190a62SNicolas Souchu 
4184e190a62SNicolas Souchu 	if (status & LAB)
4194e190a62SNicolas Souchu 		printf("pcf%d: bus arbitration lost!\n", unit);
4204e190a62SNicolas Souchu 
4214e190a62SNicolas Souchu 	if (status & BER) {
4224e190a62SNicolas Souchu 		error = IIC_EBUSERR;
4234e190a62SNicolas Souchu 		iicbus_intr(pcf->iicbus, INTR_ERROR, &error);
4244e190a62SNicolas Souchu 
4254e190a62SNicolas Souchu 		goto error;
4264e190a62SNicolas Souchu 	}
4274e190a62SNicolas Souchu 
4284e190a62SNicolas Souchu 	do {
4294e190a62SNicolas Souchu 		status = PCF_GET_S1(pcf);
4304e190a62SNicolas Souchu 
4314e190a62SNicolas Souchu 		switch(pcf->pcf_slave_mode) {
4324e190a62SNicolas Souchu 
4334e190a62SNicolas Souchu 		case SLAVE_TRANSMITTER:
4344e190a62SNicolas Souchu 			if (status & LRB) {
4354e190a62SNicolas Souchu 				/* ack interrupt line */
4364e190a62SNicolas Souchu 				dummy_write(pcf);
4374e190a62SNicolas Souchu 
4384e190a62SNicolas Souchu 				/* no ack, don't send anymore */
4394e190a62SNicolas Souchu 				pcf->pcf_slave_mode = SLAVE_RECEIVER;
4404e190a62SNicolas Souchu 
4414e190a62SNicolas Souchu 				iicbus_intr(pcf->iicbus, INTR_NOACK, NULL);
4424e190a62SNicolas Souchu 				break;
4434e190a62SNicolas Souchu 			}
4444e190a62SNicolas Souchu 
4454e190a62SNicolas Souchu 			/* get data from upper code */
4464e190a62SNicolas Souchu 			iicbus_intr(pcf->iicbus, INTR_TRANSMIT, &data);
4474e190a62SNicolas Souchu 
4484e190a62SNicolas Souchu 			PCF_SET_S0(pcf, data);
4494e190a62SNicolas Souchu 			break;
4504e190a62SNicolas Souchu 
4514e190a62SNicolas Souchu 		case SLAVE_RECEIVER:
4524e190a62SNicolas Souchu 			if (status & AAS) {
4534e190a62SNicolas Souchu 				addr = PCF_GET_S0(pcf);
4544e190a62SNicolas Souchu 
4554e190a62SNicolas Souchu 				if (status & AD0)
4564e190a62SNicolas Souchu 					iicbus_intr(pcf->iicbus, INTR_GENERAL, &addr);
4574e190a62SNicolas Souchu 				else
4584e190a62SNicolas Souchu 					iicbus_intr(pcf->iicbus, INTR_START, &addr);
4594e190a62SNicolas Souchu 
4604e190a62SNicolas Souchu 				if (addr & LSB) {
4614e190a62SNicolas Souchu 					pcf->pcf_slave_mode = SLAVE_TRANSMITTER;
4624e190a62SNicolas Souchu 
4634e190a62SNicolas Souchu 					/* get the first char from upper code */
4644e190a62SNicolas Souchu 					iicbus_intr(pcf->iicbus, INTR_TRANSMIT, &data);
4654e190a62SNicolas Souchu 
4664e190a62SNicolas Souchu 					/* send first data byte */
4674e190a62SNicolas Souchu 					PCF_SET_S0(pcf, data);
4684e190a62SNicolas Souchu 				}
4694e190a62SNicolas Souchu 
4704e190a62SNicolas Souchu 				break;
4714e190a62SNicolas Souchu 			}
4724e190a62SNicolas Souchu 
4734e190a62SNicolas Souchu 			/* stop condition received? */
4744e190a62SNicolas Souchu 			if (status & STS) {
4754e190a62SNicolas Souchu 				/* ack interrupt line */
4764e190a62SNicolas Souchu 				dummy_read(pcf);
4774e190a62SNicolas Souchu 
4784e190a62SNicolas Souchu 				/* emulate intr stop condition */
4794e190a62SNicolas Souchu 				iicbus_intr(pcf->iicbus, INTR_STOP, NULL);
4804e190a62SNicolas Souchu 
4814e190a62SNicolas Souchu 			} else {
4824e190a62SNicolas Souchu 				/* get data, ack interrupt line */
4834e190a62SNicolas Souchu 				data = PCF_GET_S0(pcf);
4844e190a62SNicolas Souchu 
4854e190a62SNicolas Souchu 				/* deliver the character */
4864e190a62SNicolas Souchu 				iicbus_intr(pcf->iicbus, INTR_RECEIVE, &data);
4874e190a62SNicolas Souchu 			}
4884e190a62SNicolas Souchu 			break;
4894e190a62SNicolas Souchu 
4904e190a62SNicolas Souchu 		    default:
4914e190a62SNicolas Souchu 			panic("%s: unknown slave mode (%d)!", __FUNCTION__,
4924e190a62SNicolas Souchu 				pcf->pcf_slave_mode);
4934e190a62SNicolas Souchu 		    }
4944e190a62SNicolas Souchu 
4954e190a62SNicolas Souchu 	} while ((PCF_GET_S1(pcf) & PIN) == 0);
4964e190a62SNicolas Souchu 
4974e190a62SNicolas Souchu 	return;
4984e190a62SNicolas Souchu 
4994e190a62SNicolas Souchu error:
5004e190a62SNicolas Souchu 	/* unknown event on bus...reset PCF */
5014e190a62SNicolas Souchu 	PCF_SET_S1(pcf, PIN|ES0|ENI|ACK);
5024e190a62SNicolas Souchu 
5034e190a62SNicolas Souchu 	pcf->pcf_slave_mode = SLAVE_RECEIVER;
5044e190a62SNicolas Souchu 
5054e190a62SNicolas Souchu 	return;
5064e190a62SNicolas Souchu }
5074e190a62SNicolas Souchu 
508af548787SNicolas Souchu static int pcf_rst_card(device_t pcfdev, u_char speed, u_char addr, u_char *oldaddr)
5094e190a62SNicolas Souchu {
5104e190a62SNicolas Souchu 	struct pcf_softc *pcf = DEVTOSOFTC(pcfdev);
511af548787SNicolas Souchu 
512af548787SNicolas Souchu 	if (oldaddr)
513af548787SNicolas Souchu 		*oldaddr = pcf->pcf_addr;
5144e190a62SNicolas Souchu 
5154e190a62SNicolas Souchu 	/* retrieve own address from bus level */
516af548787SNicolas Souchu 	if (!addr)
517af548787SNicolas Souchu 		pcf->pcf_addr = PCF_DEFAULT_ADDR;
518af548787SNicolas Souchu 	else
519af548787SNicolas Souchu 		pcf->pcf_addr = addr;
5204e190a62SNicolas Souchu 
5214e190a62SNicolas Souchu 	PCF_SET_S1(pcf, PIN);				/* initialize S1 */
5224e190a62SNicolas Souchu 
5234e190a62SNicolas Souchu 	/* own address S'O<>0 */
524af548787SNicolas Souchu 	PCF_SET_S0(pcf, pcf->pcf_addr >> 1);
5254e190a62SNicolas Souchu 
5264e190a62SNicolas Souchu 	/* select clock register */
5274e190a62SNicolas Souchu 	PCF_SET_S1(pcf, PIN|ES1);
5284e190a62SNicolas Souchu 
5294e190a62SNicolas Souchu 	/* select bus speed : 18=90kb, 19=45kb, 1A=11kb, 1B=1.5kb */
5304e190a62SNicolas Souchu 	switch (speed) {
5314e190a62SNicolas Souchu 	case IIC_SLOW:
5324e190a62SNicolas Souchu 		PCF_SET_S0(pcf,  0x1b);
5334e190a62SNicolas Souchu 		break;
5344e190a62SNicolas Souchu 
5354e190a62SNicolas Souchu 	case IIC_FAST:
5364e190a62SNicolas Souchu 		PCF_SET_S0(pcf,  0x19);
5374e190a62SNicolas Souchu 		break;
5384e190a62SNicolas Souchu 
5394e190a62SNicolas Souchu 	case IIC_UNKNOWN:
5404e190a62SNicolas Souchu 	case IIC_FASTEST:
5414e190a62SNicolas Souchu 	default:
5424e190a62SNicolas Souchu 		PCF_SET_S0(pcf,  0x18);
5434e190a62SNicolas Souchu 		break;
5444e190a62SNicolas Souchu 	}
5454e190a62SNicolas Souchu 
5464e190a62SNicolas Souchu 	/* set bus on, ack=yes, INT=yes */
5474e190a62SNicolas Souchu 	PCF_SET_S1(pcf, PIN|ES0|ENI|ACK);
5484e190a62SNicolas Souchu 
5494e190a62SNicolas Souchu 	pcf->pcf_slave_mode = SLAVE_RECEIVER;
5504e190a62SNicolas Souchu 
5514e190a62SNicolas Souchu 	return (0);
5524e190a62SNicolas Souchu }
5534e190a62SNicolas Souchu 
5544e190a62SNicolas Souchu static int
555af548787SNicolas Souchu pcf_write(device_t pcfdev, char *buf, int len, int *sent, int timeout /* us */)
5564e190a62SNicolas Souchu {
5574e190a62SNicolas Souchu 	struct pcf_softc *pcf = DEVTOSOFTC(pcfdev);
5584e190a62SNicolas Souchu 	int bytes, error = 0;
5594e190a62SNicolas Souchu 
5604e190a62SNicolas Souchu #ifdef PCFDEBUG
5614e190a62SNicolas Souchu 	printf("pcf%d: >> writing %d bytes\n", device_get_unit(pcfdev), len);
5624e190a62SNicolas Souchu #endif
5634e190a62SNicolas Souchu 
5644e190a62SNicolas Souchu 	bytes = 0;
5654e190a62SNicolas Souchu 	while (len) {
5664e190a62SNicolas Souchu 
5674e190a62SNicolas Souchu 		PCF_SET_S0(pcf, *buf++);
5684e190a62SNicolas Souchu 
569af548787SNicolas Souchu 		/* wait for the byte to be send */
5704e190a62SNicolas Souchu 		if ((error = pcf_wait_byte(pcf)))
5714e190a62SNicolas Souchu 			goto error;
5724e190a62SNicolas Souchu 
573af548787SNicolas Souchu 		/* check if ack received */
574af548787SNicolas Souchu 		if (pcf_noack(pcf, timeout)) {
5754e190a62SNicolas Souchu 			error = IIC_ENOACK;
5764e190a62SNicolas Souchu 			goto error;
5774e190a62SNicolas Souchu 		}
5784e190a62SNicolas Souchu 
5794e190a62SNicolas Souchu 		len --;
5804e190a62SNicolas Souchu 		bytes ++;
5814e190a62SNicolas Souchu 	}
5824e190a62SNicolas Souchu 
5834e190a62SNicolas Souchu error:
5844e190a62SNicolas Souchu 	*sent = bytes;
5854e190a62SNicolas Souchu 
5864e190a62SNicolas Souchu #ifdef PCFDEBUG
5874e190a62SNicolas Souchu 	printf("pcf%d: >> %d bytes written (%d)\n",
5884e190a62SNicolas Souchu 		device_get_unit(pcfdev), bytes, error);
5894e190a62SNicolas Souchu #endif
5904e190a62SNicolas Souchu 
5914e190a62SNicolas Souchu 	return (error);
5924e190a62SNicolas Souchu }
5934e190a62SNicolas Souchu 
5944e190a62SNicolas Souchu static int
595af548787SNicolas Souchu pcf_read(device_t pcfdev, char *buf, int len, int *read, int last,
596af548787SNicolas Souchu 							int delay /* us */)
5974e190a62SNicolas Souchu {
5984e190a62SNicolas Souchu 	struct pcf_softc *pcf = DEVTOSOFTC(pcfdev);
5994e190a62SNicolas Souchu 	int bytes, error = 0;
6004e190a62SNicolas Souchu 
6014e190a62SNicolas Souchu #ifdef PCFDEBUG
6024e190a62SNicolas Souchu 	printf("pcf%d: << reading %d bytes\n", device_get_unit(pcfdev), len);
6034e190a62SNicolas Souchu #endif
6044e190a62SNicolas Souchu 
6054e190a62SNicolas Souchu 	/* trig the bus to get the first data byte in S0 */
6064e190a62SNicolas Souchu 	if (len) {
607af548787SNicolas Souchu 		if (len == 1 && last)
6084e190a62SNicolas Souchu 			/* just one byte to read */
6094e190a62SNicolas Souchu 			PCF_SET_S1(pcf, ES0);		/* no ack */
6104e190a62SNicolas Souchu 
6114e190a62SNicolas Souchu 		dummy_read(pcf);
6124e190a62SNicolas Souchu 	}
6134e190a62SNicolas Souchu 
6144e190a62SNicolas Souchu 	bytes = 0;
6154e190a62SNicolas Souchu 	while (len) {
6164e190a62SNicolas Souchu 
617af548787SNicolas Souchu 		/* XXX delay needed here */
618af548787SNicolas Souchu 
619af548787SNicolas Souchu 		/* wait for trigged byte */
6204e190a62SNicolas Souchu 		if ((error = pcf_wait_byte(pcf))) {
6214e190a62SNicolas Souchu 			pcf_stop(pcfdev);
6224e190a62SNicolas Souchu 			goto error;
6234e190a62SNicolas Souchu 		}
6244e190a62SNicolas Souchu 
625af548787SNicolas Souchu 		if (len == 1 && last)
626af548787SNicolas Souchu 			/* ok, last data byte already in S0, no I2C activity
627af548787SNicolas Souchu 			 * on next PCF_GET_S0() */
6284e190a62SNicolas Souchu 			pcf_stop(pcfdev);
6294e190a62SNicolas Souchu 
630af548787SNicolas Souchu 		else if (len == 2 && last)
6314e190a62SNicolas Souchu 			/* next trigged byte with no ack */
6324e190a62SNicolas Souchu 			PCF_SET_S1(pcf, ES0);
6334e190a62SNicolas Souchu 
634af548787SNicolas Souchu 		/* receive byte, trig next byte */
6354e190a62SNicolas Souchu 		*buf++ = PCF_GET_S0(pcf);
6364e190a62SNicolas Souchu 
6374e190a62SNicolas Souchu 		len --;
6384e190a62SNicolas Souchu 		bytes ++;
6394e190a62SNicolas Souchu 	};
6404e190a62SNicolas Souchu 
6414e190a62SNicolas Souchu error:
6424e190a62SNicolas Souchu 	*read = bytes;
6434e190a62SNicolas Souchu 
6444e190a62SNicolas Souchu #ifdef PCFDEBUG
6454e190a62SNicolas Souchu 	printf("pcf%d: << %d bytes read (%d)\n",
6464e190a62SNicolas Souchu 		device_get_unit(pcfdev), bytes, error);
6474e190a62SNicolas Souchu #endif
6484e190a62SNicolas Souchu 
6494e190a62SNicolas Souchu 	return (error);
6504e190a62SNicolas Souchu }
6514e190a62SNicolas Souchu 
6524e190a62SNicolas Souchu DRIVER_MODULE(pcf, root, pcf_driver, pcf_devclass, 0, 0);
653