pcf.c revision 49195
138781Snsouch/*-
238781Snsouch * Copyright (c) 1998 Nicolas Souchu, Marc Bouget
338781Snsouch * All rights reserved.
438781Snsouch *
538781Snsouch * Redistribution and use in source and binary forms, with or without
638781Snsouch * modification, are permitted provided that the following conditions
738781Snsouch * are met:
838781Snsouch * 1. Redistributions of source code must retain the above copyright
938781Snsouch *    notice, this list of conditions and the following disclaimer.
1038781Snsouch * 2. Redistributions in binary form must reproduce the above copyright
1138781Snsouch *    notice, this list of conditions and the following disclaimer in the
1238781Snsouch *    documentation and/or other materials provided with the distribution.
1338781Snsouch *
1438781Snsouch * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1538781Snsouch * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1638781Snsouch * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1738781Snsouch * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1838781Snsouch * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1938781Snsouch * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2038781Snsouch * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2138781Snsouch * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2238781Snsouch * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2338781Snsouch * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2438781Snsouch * SUCH DAMAGE.
2538781Snsouch *
2649195Smdodd *	$Id: pcf.c,v 1.9 1999/05/08 21:59:27 dfr Exp $
2738781Snsouch *
2838781Snsouch */
2938781Snsouch#include <sys/param.h>
3038781Snsouch#include <sys/systm.h>
3138781Snsouch#include <sys/kernel.h>
3238781Snsouch#include <sys/module.h>
3338781Snsouch#include <sys/bus.h>
3438781Snsouch#include <sys/conf.h>
3538781Snsouch#include <sys/malloc.h>
3638781Snsouch
3738781Snsouch#include <machine/clock.h>
3838781Snsouch
3938781Snsouch#include <i386/isa/isa_device.h>
4038781Snsouch
4138781Snsouch#include <dev/iicbus/iiconf.h>
4238781Snsouch#include "iicbus_if.h"
4338781Snsouch
4440784Snsouch#define TIMEOUT	9999					/* XXX */
4538781Snsouch
4638781Snsouch/* Status bits of S1 register (read only) */
4738781Snsouch#define nBB	0x01		/* busy when low set/reset by STOP/START*/
4838781Snsouch#define LAB	0x02		/* lost arbitration bit in multi-master mode */
4938781Snsouch#define AAS	0x04		/* addressed as slave */
5038781Snsouch#define LRB	0x08		/* last received byte when not AAS */
5138781Snsouch#define AD0	0x08		/* general call received when AAS */
5238781Snsouch#define BER	0x10		/* bus error, misplaced START or STOP */
5338781Snsouch#define STS	0x20		/* STOP detected in slave receiver mode */
5438781Snsouch#define PIN	0x80		/* pending interrupt not (r/w) */
5538781Snsouch
5638781Snsouch/* Control bits of S1 register (write only) */
5738781Snsouch#define ACK	0x01
5838781Snsouch#define STO	0x02
5938781Snsouch#define STA	0x04
6038781Snsouch#define ENI	0x08
6138781Snsouch#define ES2	0x10
6238781Snsouch#define ES1	0x20
6338781Snsouch#define ES0	0x40
6438781Snsouch
6538781Snsouch#define BUFSIZE 2048
6638781Snsouch
6738781Snsouch#define SLAVE_TRANSMITTER	0x1
6838781Snsouch#define SLAVE_RECEIVER		0x2
6938781Snsouch
7040784Snsouch#define PCF_DEFAULT_ADDR	0xaa
7140784Snsouch
7238781Snsouchstruct pcf_softc {
7338781Snsouch
7438781Snsouch	int pcf_base;			/* isa port */
7540784Snsouch	u_char pcf_addr;		/* interface I2C address */
7638781Snsouch
7738781Snsouch	int pcf_slave_mode;		/* receiver or transmitter */
7840784Snsouch	int pcf_started;		/* 1 if start condition sent */
7938781Snsouch
8038781Snsouch	device_t iicbus;		/* the corresponding iicbus */
8138781Snsouch};
8238781Snsouch
8338781Snsouchstruct pcf_isa_softc {
8438781Snsouch
8538781Snsouch	int pcf_unit;			/* unit of the isa device */
8638781Snsouch	int pcf_base;			/* isa port */
8738781Snsouch	int pcf_irq;			/* isa irq or null if polled */
8838781Snsouch
8938781Snsouch	unsigned int pcf_flags;		/* boot flags */
9038781Snsouch};
9138781Snsouch
9238781Snsouch#define MAXPCF 2
9338781Snsouch
9438781Snsouchstatic struct pcf_isa_softc *pcfdata[MAXPCF];
9546573Speterstatic int npcf = 0;
9638781Snsouch
9738781Snsouchstatic int	pcfprobe_isa(struct isa_device *);
9838781Snsouchstatic int	pcfattach_isa(struct isa_device *);
9938781Snsouch
10038781Snsouchstruct isa_driver pcfdriver = {
10138781Snsouch	pcfprobe_isa, pcfattach_isa, "pcf"
10238781Snsouch};
10338781Snsouch
10438781Snsouchstatic int pcf_probe(device_t);
10538781Snsouchstatic int pcf_attach(device_t);
10649195Smdoddstatic int pcf_print_child(device_t, device_t);
10738781Snsouch
10840784Snsouchstatic int pcf_repeated_start(device_t, u_char, int);
10940784Snsouchstatic int pcf_start(device_t, u_char, int);
11038781Snsouchstatic int pcf_stop(device_t);
11140788Speterstatic int pcf_write(device_t, char *, int, int *, int);
11240788Speterstatic int pcf_read(device_t, char *, int, int *, int, int);
11340565Sbdestatic ointhand2_t pcfintr;
11440788Speterstatic int pcf_rst_card(device_t, u_char, u_char, u_char *);
11538781Snsouch
11638781Snsouchstatic device_method_t pcf_methods[] = {
11738781Snsouch	/* device interface */
11838781Snsouch	DEVMETHOD(device_probe,		pcf_probe),
11938781Snsouch	DEVMETHOD(device_attach,	pcf_attach),
12038781Snsouch
12138781Snsouch	/* bus interface */
12238781Snsouch	DEVMETHOD(bus_print_child,	pcf_print_child),
12338781Snsouch
12438781Snsouch	/* iicbus interface */
12540784Snsouch	DEVMETHOD(iicbus_callback,	iicbus_null_callback),
12638781Snsouch	DEVMETHOD(iicbus_repeated_start, pcf_repeated_start),
12738781Snsouch	DEVMETHOD(iicbus_start,		pcf_start),
12838781Snsouch	DEVMETHOD(iicbus_stop,		pcf_stop),
12938781Snsouch	DEVMETHOD(iicbus_write,		pcf_write),
13038781Snsouch	DEVMETHOD(iicbus_read,		pcf_read),
13138781Snsouch	DEVMETHOD(iicbus_reset,		pcf_rst_card),
13238781Snsouch
13338781Snsouch	{ 0, 0 }
13438781Snsouch};
13538781Snsouch
13638781Snsouchstatic driver_t pcf_driver = {
13738781Snsouch	"pcf",
13838781Snsouch	pcf_methods,
13938781Snsouch	sizeof(struct pcf_softc),
14038781Snsouch};
14138781Snsouch
14238781Snsouchstatic devclass_t pcf_devclass;
14338781Snsouch
14438781Snsouch#define DEVTOSOFTC(dev) ((struct pcf_softc *)device_get_softc(dev))
14538781Snsouch
14638781Snsouchstatic int
14738781Snsouchpcfprobe_isa(struct isa_device *dvp)
14838781Snsouch{
14938781Snsouch	device_t pcfdev;
15038781Snsouch	struct pcf_isa_softc *pcf;
15138781Snsouch
15238781Snsouch	if (npcf >= MAXPCF)
15338781Snsouch		return (0);
15438781Snsouch
15538781Snsouch	if ((pcf = (struct pcf_isa_softc *)malloc(sizeof(struct pcf_isa_softc),
15638781Snsouch			M_DEVBUF, M_NOWAIT)) == NULL)
15738781Snsouch		return (0);
15838781Snsouch
15938781Snsouch	pcf->pcf_base = dvp->id_iobase;		/* XXX should be ivars */
16038781Snsouch	pcf->pcf_unit = dvp->id_unit;
16138781Snsouch
16238781Snsouch	if (!(dvp->id_flags & IIC_POLLED))
16338781Snsouch		pcf->pcf_irq = (dvp->id_irq);
16438781Snsouch
16538781Snsouch	pcfdata[npcf++] = pcf;
16638781Snsouch
16738781Snsouch	/* XXX add the pcf device to the root_bus until isa bus exists */
16838781Snsouch	pcfdev = device_add_child(root_bus, "pcf", pcf->pcf_unit, NULL);
16938781Snsouch
17038781Snsouch	if (!pcfdev)
17138781Snsouch		goto error;
17238781Snsouch
17338781Snsouch	return (1);
17438781Snsouch
17538781Snsoucherror:
17638781Snsouch	free(pcf, M_DEVBUF);
17738781Snsouch	return (0);
17838781Snsouch}
17938781Snsouch
18038781Snsouchstatic int
18138781Snsouchpcfattach_isa(struct isa_device *isdp)
18238781Snsouch{
18340565Sbde	isdp->id_ointr = pcfintr;
18438781Snsouch	return (1);				/* ok */
18538781Snsouch}
18638781Snsouch
18738781Snsouchstatic int
18838781Snsouchpcf_probe(device_t pcfdev)
18938781Snsouch{
19038781Snsouch	struct pcf_softc *pcf = (struct pcf_softc *)device_get_softc(pcfdev);
19140784Snsouch	int unit = device_get_unit(pcfdev);
19238781Snsouch
19340784Snsouch	/* retrieve base address from isa initialization
19440784Snsouch	 *
19540784Snsouch	 * XXX should use ivars with isabus
19640784Snsouch	 */
19740784Snsouch	pcf->pcf_base = pcfdata[unit]->pcf_base;
19840784Snsouch
19940784Snsouch	/* reset the chip */
20040784Snsouch	pcf_rst_card(pcfdev, IIC_FASTEST, PCF_DEFAULT_ADDR, NULL);
20140784Snsouch
20238781Snsouch	/* XXX try do detect chipset */
20338781Snsouch
20438781Snsouch	device_set_desc(pcfdev, "PCF8584 I2C bus controller");
20538781Snsouch
20638781Snsouch	return (0);
20738781Snsouch}
20838781Snsouch
20938781Snsouchstatic int
21038781Snsouchpcf_attach(device_t pcfdev)
21138781Snsouch{
21238781Snsouch	struct pcf_softc *pcf = (struct pcf_softc *)device_get_softc(pcfdev);
21338781Snsouch
21440784Snsouch	pcf->iicbus = iicbus_alloc_bus(pcfdev);
21538781Snsouch
21638781Snsouch	/* probe and attach the iicbus */
21738781Snsouch	device_probe_and_attach(pcf->iicbus);
21838781Snsouch
21938781Snsouch	return (0);
22038781Snsouch}
22138781Snsouch
22249195Smdoddstatic int
22338781Snsouchpcf_print_child(device_t bus, device_t dev)
22438781Snsouch{
22540784Snsouch	struct pcf_softc *pcf = (struct pcf_softc *)device_get_softc(bus);
22649195Smdodd	int retval = 0;
22740784Snsouch
22849195Smdodd	retval += bus_print_child_header(bus, dev);
22949195Smdodd	retval += printf(" on %s addr 0x%x\n", device_get_nameunit(bus),
23049195Smdodd			 (int)pcf->pcf_addr);
23138781Snsouch
23249195Smdodd	return (retval);
23338781Snsouch}
23438781Snsouch
23538781Snsouch/*
23638781Snsouch * PCF8584 datasheet : when operate at 8 MHz or more, a minimun time of
23738781Snsouch * 6 clocks cycles must be left between two consecutives access
23838781Snsouch */
23938781Snsouch#define pcf_nops()	DELAY(10)
24038781Snsouch
24138781Snsouch#define dummy_read(pcf)		PCF_GET_S0(pcf)
24238781Snsouch#define dummy_write(pcf)	PCF_SET_S0(pcf, 0)
24338781Snsouch
24438781Snsouch/*
24538781Snsouch * Specific register access to PCF8584
24638781Snsouch */
24738781Snsouchstatic void PCF_SET_S0(struct pcf_softc *pcf, int data)
24838781Snsouch{
24938781Snsouch	outb(pcf->pcf_base, data);
25038781Snsouch	pcf_nops();
25138781Snsouch}
25238781Snsouch
25338781Snsouchstatic void PCF_SET_S1(struct pcf_softc *pcf, int data)
25438781Snsouch{
25538781Snsouch	outb(pcf->pcf_base+1, data);
25638781Snsouch	pcf_nops();
25738781Snsouch}
25838781Snsouch
25938781Snsouchstatic char PCF_GET_S0(struct pcf_softc *pcf)
26038781Snsouch{
26138781Snsouch	char data;
26238781Snsouch
26338781Snsouch	data = inb(pcf->pcf_base);
26438781Snsouch	pcf_nops();
26538781Snsouch
26638781Snsouch	return (data);
26738781Snsouch}
26838781Snsouch
26938781Snsouchstatic char PCF_GET_S1(struct pcf_softc *pcf)
27038781Snsouch{
27138781Snsouch	char data;
27238781Snsouch
27338781Snsouch	data = inb(pcf->pcf_base+1);
27438781Snsouch	pcf_nops();
27538781Snsouch
27638781Snsouch	return (data);
27738781Snsouch}
27838781Snsouch
27938781Snsouch/*
28038781Snsouch * Polling mode for master operations wait for a new
28138781Snsouch * byte incomming or outgoing
28238781Snsouch */
28338781Snsouchstatic int pcf_wait_byte(struct pcf_softc *pcf)
28438781Snsouch{
28538781Snsouch	int counter = TIMEOUT;
28638781Snsouch
28738781Snsouch	while (counter--) {
28838781Snsouch
28938781Snsouch		if ((PCF_GET_S1(pcf) & PIN) == 0)
29038781Snsouch			return (0);
29138781Snsouch	}
29238781Snsouch
29338781Snsouch	return (IIC_ETIMEOUT);
29438781Snsouch}
29538781Snsouch
29638781Snsouchstatic int pcf_stop(device_t pcfdev)
29738781Snsouch{
29838781Snsouch	struct pcf_softc *pcf = DEVTOSOFTC(pcfdev);
29938781Snsouch
30040784Snsouch	/*
30140784Snsouch	 * Send STOP condition iff the START condition was previously sent.
30240784Snsouch	 * STOP is sent only once even if a iicbus_stop() is called after
30340784Snsouch	 * an iicbus_read()... see pcf_read(): the pcf needs to send the stop
30440784Snsouch	 * before the last char is read.
30540784Snsouch	 */
30640784Snsouch	if (pcf->pcf_started) {
30740784Snsouch		/* set stop condition and enable IT */
30840784Snsouch		PCF_SET_S1(pcf, PIN|ES0|ENI|STO|ACK);
30938781Snsouch
31040784Snsouch		pcf->pcf_started = 0;
31140784Snsouch	}
31240784Snsouch
31338781Snsouch	return (0);
31438781Snsouch}
31538781Snsouch
31640784Snsouch
31740784Snsouchstatic int pcf_noack(struct pcf_softc *pcf, int timeout)
31838781Snsouch{
31940784Snsouch	int noack;
32040784Snsouch	int k = timeout/10;
32140784Snsouch
32240784Snsouch	do {
32340784Snsouch		noack = PCF_GET_S1(pcf) & LRB;
32440784Snsouch		if (!noack)
32540784Snsouch			break;
32640784Snsouch		DELAY(10);				/* XXX wait 10 us */
32740784Snsouch	} while (k--);
32840784Snsouch
32940784Snsouch	return (noack);
33040784Snsouch}
33140784Snsouch
33240784Snsouchstatic int pcf_repeated_start(device_t pcfdev, u_char slave, int timeout)
33340784Snsouch{
33438781Snsouch	struct pcf_softc *pcf = DEVTOSOFTC(pcfdev);
33538781Snsouch	int error = 0;
33638781Snsouch
33738781Snsouch	/* repeated start */
33838781Snsouch	PCF_SET_S1(pcf, ES0|STA|STO|ACK);
33938781Snsouch
34038781Snsouch	/* set slave address to PCF. Last bit (LSB) must be set correctly
34138781Snsouch	 * according to transfer direction */
34238781Snsouch	PCF_SET_S0(pcf, slave);
34338781Snsouch
34438781Snsouch	/* wait for address sent, polling */
34538781Snsouch	if ((error = pcf_wait_byte(pcf)))
34638781Snsouch		goto error;
34738781Snsouch
34840784Snsouch	/* check for ack */
34940784Snsouch	if (pcf_noack(pcf, timeout)) {
35038781Snsouch		error = IIC_ENOACK;
35138781Snsouch		goto error;
35238781Snsouch	}
35338781Snsouch
35438781Snsouch	return (0);
35538781Snsouch
35638781Snsoucherror:
35738781Snsouch	pcf_stop(pcfdev);
35838781Snsouch	return (error);
35938781Snsouch}
36038781Snsouch
36140784Snsouchstatic int pcf_start(device_t pcfdev, u_char slave, int timeout)
36238781Snsouch{
36338781Snsouch	struct pcf_softc *pcf = DEVTOSOFTC(pcfdev);
36438781Snsouch	int error = 0;
36538781Snsouch
36646343Speter	if ((PCF_GET_S1(pcf) & nBB) == 0)
36738781Snsouch		return (IIC_EBUSBSY);
36838781Snsouch
36938781Snsouch	/* set slave address to PCF. Last bit (LSB) must be set correctly
37038781Snsouch	 * according to transfer direction */
37138781Snsouch	PCF_SET_S0(pcf, slave);
37238781Snsouch
37338781Snsouch	/* START only */
37438781Snsouch	PCF_SET_S1(pcf, PIN|ES0|STA|ACK);
37538781Snsouch
37640784Snsouch	pcf->pcf_started = 1;
37740784Snsouch
37838781Snsouch	/* wait for address sent, polling */
37938781Snsouch	if ((error = pcf_wait_byte(pcf)))
38038781Snsouch		goto error;
38138781Snsouch
38240784Snsouch	/* check for ACK */
38340784Snsouch	if (pcf_noack(pcf, timeout)) {
38438781Snsouch		error = IIC_ENOACK;
38538781Snsouch		goto error;
38638781Snsouch	}
38738781Snsouch
38838781Snsouch	return (0);
38938781Snsouch
39038781Snsoucherror:
39138781Snsouch	pcf_stop(pcfdev);
39238781Snsouch	return (error);
39338781Snsouch}
39438781Snsouch
39540565Sbdestatic void
39638781Snsouchpcfintr(unit)
39738781Snsouch{
39838781Snsouch	struct pcf_softc *pcf =
39938781Snsouch		(struct pcf_softc *)devclass_get_softc(pcf_devclass, unit);
40038781Snsouch
40138781Snsouch	char data, status, addr;
40238781Snsouch	char error = 0;
40338781Snsouch
40438781Snsouch	status = PCF_GET_S1(pcf);
40538781Snsouch
40638781Snsouch	if (status & PIN) {
40738781Snsouch		printf("pcf%d: spurious interrupt, status=0x%x\n", unit,
40838781Snsouch			status & 0xff);
40938781Snsouch
41038781Snsouch		goto error;
41138781Snsouch	}
41238781Snsouch
41338781Snsouch	if (status & LAB)
41438781Snsouch		printf("pcf%d: bus arbitration lost!\n", unit);
41538781Snsouch
41638781Snsouch	if (status & BER) {
41738781Snsouch		error = IIC_EBUSERR;
41838781Snsouch		iicbus_intr(pcf->iicbus, INTR_ERROR, &error);
41938781Snsouch
42038781Snsouch		goto error;
42138781Snsouch	}
42238781Snsouch
42338781Snsouch	do {
42438781Snsouch		status = PCF_GET_S1(pcf);
42538781Snsouch
42638781Snsouch		switch(pcf->pcf_slave_mode) {
42738781Snsouch
42838781Snsouch		case SLAVE_TRANSMITTER:
42938781Snsouch			if (status & LRB) {
43038781Snsouch				/* ack interrupt line */
43138781Snsouch				dummy_write(pcf);
43238781Snsouch
43338781Snsouch				/* no ack, don't send anymore */
43438781Snsouch				pcf->pcf_slave_mode = SLAVE_RECEIVER;
43538781Snsouch
43638781Snsouch				iicbus_intr(pcf->iicbus, INTR_NOACK, NULL);
43738781Snsouch				break;
43838781Snsouch			}
43938781Snsouch
44038781Snsouch			/* get data from upper code */
44138781Snsouch			iicbus_intr(pcf->iicbus, INTR_TRANSMIT, &data);
44238781Snsouch
44338781Snsouch			PCF_SET_S0(pcf, data);
44438781Snsouch			break;
44538781Snsouch
44638781Snsouch		case SLAVE_RECEIVER:
44738781Snsouch			if (status & AAS) {
44838781Snsouch				addr = PCF_GET_S0(pcf);
44938781Snsouch
45038781Snsouch				if (status & AD0)
45138781Snsouch					iicbus_intr(pcf->iicbus, INTR_GENERAL, &addr);
45238781Snsouch				else
45338781Snsouch					iicbus_intr(pcf->iicbus, INTR_START, &addr);
45438781Snsouch
45538781Snsouch				if (addr & LSB) {
45638781Snsouch					pcf->pcf_slave_mode = SLAVE_TRANSMITTER;
45738781Snsouch
45838781Snsouch					/* get the first char from upper code */
45938781Snsouch					iicbus_intr(pcf->iicbus, INTR_TRANSMIT, &data);
46038781Snsouch
46138781Snsouch					/* send first data byte */
46238781Snsouch					PCF_SET_S0(pcf, data);
46338781Snsouch				}
46438781Snsouch
46538781Snsouch				break;
46638781Snsouch			}
46738781Snsouch
46838781Snsouch			/* stop condition received? */
46938781Snsouch			if (status & STS) {
47038781Snsouch				/* ack interrupt line */
47138781Snsouch				dummy_read(pcf);
47238781Snsouch
47338781Snsouch				/* emulate intr stop condition */
47438781Snsouch				iicbus_intr(pcf->iicbus, INTR_STOP, NULL);
47538781Snsouch
47638781Snsouch			} else {
47738781Snsouch				/* get data, ack interrupt line */
47838781Snsouch				data = PCF_GET_S0(pcf);
47938781Snsouch
48038781Snsouch				/* deliver the character */
48138781Snsouch				iicbus_intr(pcf->iicbus, INTR_RECEIVE, &data);
48238781Snsouch			}
48338781Snsouch			break;
48438781Snsouch
48538781Snsouch		    default:
48638781Snsouch			panic("%s: unknown slave mode (%d)!", __FUNCTION__,
48738781Snsouch				pcf->pcf_slave_mode);
48838781Snsouch		    }
48938781Snsouch
49038781Snsouch	} while ((PCF_GET_S1(pcf) & PIN) == 0);
49138781Snsouch
49238781Snsouch	return;
49338781Snsouch
49438781Snsoucherror:
49538781Snsouch	/* unknown event on bus...reset PCF */
49638781Snsouch	PCF_SET_S1(pcf, PIN|ES0|ENI|ACK);
49738781Snsouch
49838781Snsouch	pcf->pcf_slave_mode = SLAVE_RECEIVER;
49938781Snsouch
50038781Snsouch	return;
50138781Snsouch}
50238781Snsouch
50340784Snsouchstatic int pcf_rst_card(device_t pcfdev, u_char speed, u_char addr, u_char *oldaddr)
50438781Snsouch{
50538781Snsouch	struct pcf_softc *pcf = DEVTOSOFTC(pcfdev);
50638781Snsouch
50740784Snsouch	if (oldaddr)
50840784Snsouch		*oldaddr = pcf->pcf_addr;
50940784Snsouch
51038781Snsouch	/* retrieve own address from bus level */
51140784Snsouch	if (!addr)
51240784Snsouch		pcf->pcf_addr = PCF_DEFAULT_ADDR;
51340784Snsouch	else
51440784Snsouch		pcf->pcf_addr = addr;
51538781Snsouch
51638781Snsouch	PCF_SET_S1(pcf, PIN);				/* initialize S1 */
51738781Snsouch
51838781Snsouch	/* own address S'O<>0 */
51940784Snsouch	PCF_SET_S0(pcf, pcf->pcf_addr >> 1);
52038781Snsouch
52138781Snsouch	/* select clock register */
52238781Snsouch	PCF_SET_S1(pcf, PIN|ES1);
52338781Snsouch
52438781Snsouch	/* select bus speed : 18=90kb, 19=45kb, 1A=11kb, 1B=1.5kb */
52538781Snsouch	switch (speed) {
52638781Snsouch	case IIC_SLOW:
52738781Snsouch		PCF_SET_S0(pcf,  0x1b);
52838781Snsouch		break;
52938781Snsouch
53038781Snsouch	case IIC_FAST:
53138781Snsouch		PCF_SET_S0(pcf,  0x19);
53238781Snsouch		break;
53338781Snsouch
53438781Snsouch	case IIC_UNKNOWN:
53538781Snsouch	case IIC_FASTEST:
53638781Snsouch	default:
53738781Snsouch		PCF_SET_S0(pcf,  0x18);
53838781Snsouch		break;
53938781Snsouch	}
54038781Snsouch
54138781Snsouch	/* set bus on, ack=yes, INT=yes */
54238781Snsouch	PCF_SET_S1(pcf, PIN|ES0|ENI|ACK);
54338781Snsouch
54438781Snsouch	pcf->pcf_slave_mode = SLAVE_RECEIVER;
54538781Snsouch
54638781Snsouch	return (0);
54738781Snsouch}
54838781Snsouch
54938781Snsouchstatic int
55040784Snsouchpcf_write(device_t pcfdev, char *buf, int len, int *sent, int timeout /* us */)
55138781Snsouch{
55238781Snsouch	struct pcf_softc *pcf = DEVTOSOFTC(pcfdev);
55338781Snsouch	int bytes, error = 0;
55438781Snsouch
55538781Snsouch#ifdef PCFDEBUG
55638781Snsouch	printf("pcf%d: >> writing %d bytes\n", device_get_unit(pcfdev), len);
55738781Snsouch#endif
55838781Snsouch
55938781Snsouch	bytes = 0;
56038781Snsouch	while (len) {
56138781Snsouch
56238781Snsouch		PCF_SET_S0(pcf, *buf++);
56338781Snsouch
56440784Snsouch		/* wait for the byte to be send */
56538781Snsouch		if ((error = pcf_wait_byte(pcf)))
56638781Snsouch			goto error;
56738781Snsouch
56840784Snsouch		/* check if ack received */
56940784Snsouch		if (pcf_noack(pcf, timeout)) {
57038781Snsouch			error = IIC_ENOACK;
57138781Snsouch			goto error;
57238781Snsouch		}
57338781Snsouch
57438781Snsouch		len --;
57538781Snsouch		bytes ++;
57638781Snsouch	}
57738781Snsouch
57838781Snsoucherror:
57938781Snsouch	*sent = bytes;
58038781Snsouch
58138781Snsouch#ifdef PCFDEBUG
58238781Snsouch	printf("pcf%d: >> %d bytes written (%d)\n",
58338781Snsouch		device_get_unit(pcfdev), bytes, error);
58438781Snsouch#endif
58538781Snsouch
58638781Snsouch	return (error);
58738781Snsouch}
58838781Snsouch
58938781Snsouchstatic int
59040784Snsouchpcf_read(device_t pcfdev, char *buf, int len, int *read, int last,
59140784Snsouch							int delay /* us */)
59238781Snsouch{
59338781Snsouch	struct pcf_softc *pcf = DEVTOSOFTC(pcfdev);
59438781Snsouch	int bytes, error = 0;
59538781Snsouch
59638781Snsouch#ifdef PCFDEBUG
59738781Snsouch	printf("pcf%d: << reading %d bytes\n", device_get_unit(pcfdev), len);
59838781Snsouch#endif
59938781Snsouch
60038781Snsouch	/* trig the bus to get the first data byte in S0 */
60138781Snsouch	if (len) {
60240784Snsouch		if (len == 1 && last)
60338781Snsouch			/* just one byte to read */
60438781Snsouch			PCF_SET_S1(pcf, ES0);		/* no ack */
60538781Snsouch
60638781Snsouch		dummy_read(pcf);
60738781Snsouch	}
60838781Snsouch
60938781Snsouch	bytes = 0;
61038781Snsouch	while (len) {
61138781Snsouch
61240784Snsouch		/* XXX delay needed here */
61340784Snsouch
61440784Snsouch		/* wait for trigged byte */
61538781Snsouch		if ((error = pcf_wait_byte(pcf))) {
61638781Snsouch			pcf_stop(pcfdev);
61738781Snsouch			goto error;
61838781Snsouch		}
61938781Snsouch
62040784Snsouch		if (len == 1 && last)
62140784Snsouch			/* ok, last data byte already in S0, no I2C activity
62240784Snsouch			 * on next PCF_GET_S0() */
62338781Snsouch			pcf_stop(pcfdev);
62438781Snsouch
62540784Snsouch		else if (len == 2 && last)
62640784Snsouch			/* next trigged byte with no ack */
62740784Snsouch			PCF_SET_S1(pcf, ES0);
62838781Snsouch
62940784Snsouch		/* receive byte, trig next byte */
63040784Snsouch		*buf++ = PCF_GET_S0(pcf);
63138781Snsouch
63238781Snsouch		len --;
63338781Snsouch		bytes ++;
63438781Snsouch	};
63538781Snsouch
63638781Snsoucherror:
63738781Snsouch	*read = bytes;
63838781Snsouch
63938781Snsouch#ifdef PCFDEBUG
64038781Snsouch	printf("pcf%d: << %d bytes read (%d)\n",
64138781Snsouch		device_get_unit(pcfdev), bytes, error);
64238781Snsouch#endif
64338781Snsouch
64438781Snsouch	return (error);
64538781Snsouch}
64638781Snsouch
64738781SnsouchDRIVER_MODULE(pcf, root, pcf_driver, pcf_devclass, 0, 0);
648