pcf.c revision 108533
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 *
2650477Speter * $FreeBSD: head/sys/dev/pcf/pcf.c 108533 2003-01-01 18:49:04Z schweikh $
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
3555939Snsouch#include <machine/bus.h>
3655939Snsouch#include <machine/resource.h>
3755939Snsouch#include <sys/rman.h>
3838781Snsouch
3955939Snsouch#include <isa/isareg.h>
4055939Snsouch#include <isa/isavar.h>
4155939Snsouch
4238781Snsouch#include <i386/isa/isa_device.h>
4338781Snsouch
4438781Snsouch#include <dev/iicbus/iiconf.h>
4538781Snsouch#include "iicbus_if.h"
4638781Snsouch
4755939Snsouch#define IO_PCFSIZE	2
4855939Snsouch
4940784Snsouch#define TIMEOUT	9999					/* XXX */
5038781Snsouch
5138781Snsouch/* Status bits of S1 register (read only) */
5238781Snsouch#define nBB	0x01		/* busy when low set/reset by STOP/START*/
5338781Snsouch#define LAB	0x02		/* lost arbitration bit in multi-master mode */
5438781Snsouch#define AAS	0x04		/* addressed as slave */
5538781Snsouch#define LRB	0x08		/* last received byte when not AAS */
5638781Snsouch#define AD0	0x08		/* general call received when AAS */
5738781Snsouch#define BER	0x10		/* bus error, misplaced START or STOP */
5838781Snsouch#define STS	0x20		/* STOP detected in slave receiver mode */
5938781Snsouch#define PIN	0x80		/* pending interrupt not (r/w) */
6038781Snsouch
6138781Snsouch/* Control bits of S1 register (write only) */
6238781Snsouch#define ACK	0x01
6338781Snsouch#define STO	0x02
6438781Snsouch#define STA	0x04
6538781Snsouch#define ENI	0x08
6638781Snsouch#define ES2	0x10
6738781Snsouch#define ES1	0x20
6838781Snsouch#define ES0	0x40
6938781Snsouch
7038781Snsouch#define BUFSIZE 2048
7138781Snsouch
7238781Snsouch#define SLAVE_TRANSMITTER	0x1
7338781Snsouch#define SLAVE_RECEIVER		0x2
7438781Snsouch
7540784Snsouch#define PCF_DEFAULT_ADDR	0xaa
7640784Snsouch
7738781Snsouchstruct pcf_softc {
7838781Snsouch
7938781Snsouch	int pcf_base;			/* isa port */
8055939Snsouch	int pcf_flags;
8140784Snsouch	u_char pcf_addr;		/* interface I2C address */
8238781Snsouch
8338781Snsouch	int pcf_slave_mode;		/* receiver or transmitter */
8440784Snsouch	int pcf_started;		/* 1 if start condition sent */
8538781Snsouch
8638781Snsouch	device_t iicbus;		/* the corresponding iicbus */
8738781Snsouch
8855939Snsouch  	int rid_irq, rid_ioport;
8955939Snsouch	struct resource *res_irq, *res_ioport;
9055939Snsouch	void *intr_cookie;
9138781Snsouch};
9238781Snsouch
9338781Snsouchstatic int pcf_probe(device_t);
9438781Snsouchstatic int pcf_attach(device_t);
9555939Snsouchstatic void pcfintr(void *arg);
9655939Snsouch
9749195Smdoddstatic int pcf_print_child(device_t, device_t);
9838781Snsouch
9940784Snsouchstatic int pcf_repeated_start(device_t, u_char, int);
10040784Snsouchstatic int pcf_start(device_t, u_char, int);
10138781Snsouchstatic int pcf_stop(device_t);
10240788Speterstatic int pcf_write(device_t, char *, int, int *, int);
10340788Speterstatic int pcf_read(device_t, char *, int, int *, int, int);
10440788Speterstatic int pcf_rst_card(device_t, u_char, u_char, u_char *);
10538781Snsouch
10638781Snsouchstatic device_method_t pcf_methods[] = {
10738781Snsouch	/* device interface */
10838781Snsouch	DEVMETHOD(device_probe,		pcf_probe),
10938781Snsouch	DEVMETHOD(device_attach,	pcf_attach),
11038781Snsouch
11138781Snsouch	/* bus interface */
11238781Snsouch	DEVMETHOD(bus_print_child,	pcf_print_child),
11338781Snsouch
11438781Snsouch	/* iicbus interface */
11540784Snsouch	DEVMETHOD(iicbus_callback,	iicbus_null_callback),
11638781Snsouch	DEVMETHOD(iicbus_repeated_start, pcf_repeated_start),
11738781Snsouch	DEVMETHOD(iicbus_start,		pcf_start),
11838781Snsouch	DEVMETHOD(iicbus_stop,		pcf_stop),
11938781Snsouch	DEVMETHOD(iicbus_write,		pcf_write),
12038781Snsouch	DEVMETHOD(iicbus_read,		pcf_read),
12138781Snsouch	DEVMETHOD(iicbus_reset,		pcf_rst_card),
12238781Snsouch
12338781Snsouch	{ 0, 0 }
12438781Snsouch};
12538781Snsouch
12638781Snsouchstatic driver_t pcf_driver = {
12738781Snsouch	"pcf",
12838781Snsouch	pcf_methods,
12938781Snsouch	sizeof(struct pcf_softc),
13038781Snsouch};
13138781Snsouch
13238781Snsouchstatic devclass_t pcf_devclass;
13338781Snsouch
13438781Snsouch#define DEVTOSOFTC(dev) ((struct pcf_softc *)device_get_softc(dev))
13538781Snsouch
13638781Snsouchstatic int
13755939Snsouchpcf_probe(device_t pcfdev)
13838781Snsouch{
13955939Snsouch	struct pcf_softc *pcf = DEVTOSOFTC(pcfdev);
14055939Snsouch	device_t parent = device_get_parent(pcfdev);
14138781Snsouch
14255939Snsouch	device_set_desc(pcfdev, "PCF8584 I2C bus controller");
14338781Snsouch
14455939Snsouch	pcf = DEVTOSOFTC(pcfdev);
14555939Snsouch	bzero(pcf, sizeof(struct pcf_softc));
14638781Snsouch
14755939Snsouch	pcf->rid_irq = pcf->rid_ioport = 0;
14855939Snsouch	pcf->res_irq = pcf->res_ioport = 0;
14938781Snsouch
15055939Snsouch	/* IO port is mandatory */
15155939Snsouch	pcf->res_ioport = bus_alloc_resource(pcfdev, SYS_RES_IOPORT,
15255939Snsouch					     &pcf->rid_ioport, 0ul, ~0ul,
15355939Snsouch					     IO_PCFSIZE, RF_ACTIVE);
15455939Snsouch	if (pcf->res_ioport == 0) {
15555939Snsouch		device_printf(pcfdev, "cannot reserve I/O port range\n");
15638781Snsouch		goto error;
15755939Snsouch	}
15855939Snsouch	BUS_READ_IVAR(parent, pcfdev, ISA_IVAR_PORT, &pcf->pcf_base);
15938781Snsouch
16055939Snsouch	pcf->pcf_flags = device_get_flags(pcfdev);
16138781Snsouch
16255939Snsouch	if (!(pcf->pcf_flags & IIC_POLLED)) {
16355939Snsouch		pcf->res_irq = bus_alloc_resource(pcfdev, SYS_RES_IRQ, &pcf->rid_irq,
16455939Snsouch						  0ul, ~0ul, 1, RF_ACTIVE);
16555939Snsouch		if (pcf->res_irq == 0) {
16655939Snsouch			device_printf(pcfdev, "can't reserve irq, polled mode.\n");
16755939Snsouch			pcf->pcf_flags |= IIC_POLLED;
16855939Snsouch		}
16955939Snsouch	}
17038781Snsouch
17140784Snsouch	/* reset the chip */
17240784Snsouch	pcf_rst_card(pcfdev, IIC_FASTEST, PCF_DEFAULT_ADDR, NULL);
17340784Snsouch
17438781Snsouch	return (0);
17555939Snsoucherror:
17655939Snsouch	if (pcf->res_ioport != 0) {
17755939Snsouch		bus_deactivate_resource(pcfdev, SYS_RES_IOPORT, pcf->rid_ioport,
17855939Snsouch					pcf->res_ioport);
17955939Snsouch		bus_release_resource(pcfdev, SYS_RES_IOPORT, pcf->rid_ioport,
18055939Snsouch				     pcf->res_ioport);
18155939Snsouch	}
18255939Snsouch	return (ENXIO);
18338781Snsouch}
18438781Snsouch
18538781Snsouchstatic int
18638781Snsouchpcf_attach(device_t pcfdev)
18738781Snsouch{
18855939Snsouch	struct pcf_softc *pcf = DEVTOSOFTC(pcfdev);
18955939Snsouch	device_t parent = device_get_parent(pcfdev);
19055939Snsouch	int error = 0;
19138781Snsouch
19255939Snsouch	if (pcf->res_irq) {
19355939Snsouch		/* default to the tty mask for registration */	/* XXX */
19455939Snsouch		error = BUS_SETUP_INTR(parent, pcfdev, pcf->res_irq, INTR_TYPE_NET,
19555939Snsouch					    pcfintr, pcfdev, &pcf->intr_cookie);
19655939Snsouch		if (error)
19755939Snsouch			return (error);
19855939Snsouch	}
19955939Snsouch
20093165Snsouch	pcf->iicbus = device_add_child(pcfdev, "iicbus", -1);
20138781Snsouch
20238781Snsouch	/* probe and attach the iicbus */
20393165Snsouch	bus_generic_attach(pcfdev);
20438781Snsouch
20538781Snsouch	return (0);
20638781Snsouch}
20738781Snsouch
20849195Smdoddstatic int
20938781Snsouchpcf_print_child(device_t bus, device_t dev)
21038781Snsouch{
21140784Snsouch	struct pcf_softc *pcf = (struct pcf_softc *)device_get_softc(bus);
21249195Smdodd	int retval = 0;
21340784Snsouch
21449195Smdodd	retval += bus_print_child_header(bus, dev);
21549195Smdodd	retval += printf(" on %s addr 0x%x\n", device_get_nameunit(bus),
21649195Smdodd			 (int)pcf->pcf_addr);
21738781Snsouch
21849195Smdodd	return (retval);
21938781Snsouch}
22038781Snsouch
22138781Snsouch/*
22238781Snsouch * PCF8584 datasheet : when operate at 8 MHz or more, a minimun time of
22338781Snsouch * 6 clocks cycles must be left between two consecutives access
22438781Snsouch */
22538781Snsouch#define pcf_nops()	DELAY(10)
22638781Snsouch
22738781Snsouch#define dummy_read(pcf)		PCF_GET_S0(pcf)
22838781Snsouch#define dummy_write(pcf)	PCF_SET_S0(pcf, 0)
22938781Snsouch
23038781Snsouch/*
23138781Snsouch * Specific register access to PCF8584
23238781Snsouch */
23338781Snsouchstatic void PCF_SET_S0(struct pcf_softc *pcf, int data)
23438781Snsouch{
23538781Snsouch	outb(pcf->pcf_base, data);
23638781Snsouch	pcf_nops();
23738781Snsouch}
23838781Snsouch
23938781Snsouchstatic void PCF_SET_S1(struct pcf_softc *pcf, int data)
24038781Snsouch{
24138781Snsouch	outb(pcf->pcf_base+1, data);
24238781Snsouch	pcf_nops();
24338781Snsouch}
24438781Snsouch
24538781Snsouchstatic char PCF_GET_S0(struct pcf_softc *pcf)
24638781Snsouch{
24738781Snsouch	char data;
24838781Snsouch
24938781Snsouch	data = inb(pcf->pcf_base);
25038781Snsouch	pcf_nops();
25138781Snsouch
25238781Snsouch	return (data);
25338781Snsouch}
25438781Snsouch
25538781Snsouchstatic char PCF_GET_S1(struct pcf_softc *pcf)
25638781Snsouch{
25738781Snsouch	char data;
25838781Snsouch
25938781Snsouch	data = inb(pcf->pcf_base+1);
26038781Snsouch	pcf_nops();
26138781Snsouch
26238781Snsouch	return (data);
26338781Snsouch}
26438781Snsouch
26538781Snsouch/*
26638781Snsouch * Polling mode for master operations wait for a new
26738781Snsouch * byte incomming or outgoing
26838781Snsouch */
26938781Snsouchstatic int pcf_wait_byte(struct pcf_softc *pcf)
27038781Snsouch{
27138781Snsouch	int counter = TIMEOUT;
27238781Snsouch
27338781Snsouch	while (counter--) {
27438781Snsouch
27538781Snsouch		if ((PCF_GET_S1(pcf) & PIN) == 0)
27638781Snsouch			return (0);
27738781Snsouch	}
27838781Snsouch
27938781Snsouch	return (IIC_ETIMEOUT);
28038781Snsouch}
28138781Snsouch
28238781Snsouchstatic int pcf_stop(device_t pcfdev)
28338781Snsouch{
28438781Snsouch	struct pcf_softc *pcf = DEVTOSOFTC(pcfdev);
28538781Snsouch
28640784Snsouch	/*
28740784Snsouch	 * Send STOP condition iff the START condition was previously sent.
288108533Sschweikh	 * STOP is sent only once even if an iicbus_stop() is called after
28940784Snsouch	 * an iicbus_read()... see pcf_read(): the pcf needs to send the stop
29040784Snsouch	 * before the last char is read.
29140784Snsouch	 */
29240784Snsouch	if (pcf->pcf_started) {
29340784Snsouch		/* set stop condition and enable IT */
29440784Snsouch		PCF_SET_S1(pcf, PIN|ES0|ENI|STO|ACK);
29538781Snsouch
29640784Snsouch		pcf->pcf_started = 0;
29740784Snsouch	}
29840784Snsouch
29938781Snsouch	return (0);
30038781Snsouch}
30138781Snsouch
30240784Snsouch
30340784Snsouchstatic int pcf_noack(struct pcf_softc *pcf, int timeout)
30438781Snsouch{
30540784Snsouch	int noack;
30640784Snsouch	int k = timeout/10;
30740784Snsouch
30840784Snsouch	do {
30940784Snsouch		noack = PCF_GET_S1(pcf) & LRB;
31040784Snsouch		if (!noack)
31140784Snsouch			break;
31240784Snsouch		DELAY(10);				/* XXX wait 10 us */
31340784Snsouch	} while (k--);
31440784Snsouch
31540784Snsouch	return (noack);
31640784Snsouch}
31740784Snsouch
31840784Snsouchstatic int pcf_repeated_start(device_t pcfdev, u_char slave, int timeout)
31940784Snsouch{
32038781Snsouch	struct pcf_softc *pcf = DEVTOSOFTC(pcfdev);
32138781Snsouch	int error = 0;
32238781Snsouch
32338781Snsouch	/* repeated start */
32438781Snsouch	PCF_SET_S1(pcf, ES0|STA|STO|ACK);
32538781Snsouch
32638781Snsouch	/* set slave address to PCF. Last bit (LSB) must be set correctly
32738781Snsouch	 * according to transfer direction */
32838781Snsouch	PCF_SET_S0(pcf, slave);
32938781Snsouch
33038781Snsouch	/* wait for address sent, polling */
33138781Snsouch	if ((error = pcf_wait_byte(pcf)))
33238781Snsouch		goto error;
33338781Snsouch
33440784Snsouch	/* check for ack */
33540784Snsouch	if (pcf_noack(pcf, timeout)) {
33638781Snsouch		error = IIC_ENOACK;
33738781Snsouch		goto error;
33838781Snsouch	}
33938781Snsouch
34038781Snsouch	return (0);
34138781Snsouch
34238781Snsoucherror:
34338781Snsouch	pcf_stop(pcfdev);
34438781Snsouch	return (error);
34538781Snsouch}
34638781Snsouch
34740784Snsouchstatic int pcf_start(device_t pcfdev, u_char slave, int timeout)
34838781Snsouch{
34938781Snsouch	struct pcf_softc *pcf = DEVTOSOFTC(pcfdev);
35038781Snsouch	int error = 0;
35138781Snsouch
35246343Speter	if ((PCF_GET_S1(pcf) & nBB) == 0)
35338781Snsouch		return (IIC_EBUSBSY);
35438781Snsouch
35538781Snsouch	/* set slave address to PCF. Last bit (LSB) must be set correctly
35638781Snsouch	 * according to transfer direction */
35738781Snsouch	PCF_SET_S0(pcf, slave);
35838781Snsouch
35938781Snsouch	/* START only */
36038781Snsouch	PCF_SET_S1(pcf, PIN|ES0|STA|ACK);
36138781Snsouch
36240784Snsouch	pcf->pcf_started = 1;
36340784Snsouch
36438781Snsouch	/* wait for address sent, polling */
36538781Snsouch	if ((error = pcf_wait_byte(pcf)))
36638781Snsouch		goto error;
36738781Snsouch
36840784Snsouch	/* check for ACK */
36940784Snsouch	if (pcf_noack(pcf, timeout)) {
37038781Snsouch		error = IIC_ENOACK;
37138781Snsouch		goto error;
37238781Snsouch	}
37338781Snsouch
37438781Snsouch	return (0);
37538781Snsouch
37638781Snsoucherror:
37738781Snsouch	pcf_stop(pcfdev);
37838781Snsouch	return (error);
37938781Snsouch}
38038781Snsouch
38140565Sbdestatic void
38255939Snsouchpcfintr(void *arg)
38338781Snsouch{
38455939Snsouch	device_t pcfdev = (device_t)arg;
38555939Snsouch	struct pcf_softc *pcf = DEVTOSOFTC(pcfdev);
38638781Snsouch
38738781Snsouch	char data, status, addr;
38838781Snsouch	char error = 0;
38938781Snsouch
39038781Snsouch	status = PCF_GET_S1(pcf);
39138781Snsouch
39238781Snsouch	if (status & PIN) {
39355939Snsouch		device_printf(pcfdev, "spurious interrupt, status=0x%x\n", status & 0xff);
39438781Snsouch
39538781Snsouch		goto error;
39638781Snsouch	}
39738781Snsouch
39838781Snsouch	if (status & LAB)
39955939Snsouch		device_printf(pcfdev, "bus arbitration lost!\n");
40038781Snsouch
40138781Snsouch	if (status & BER) {
40238781Snsouch		error = IIC_EBUSERR;
40338781Snsouch		iicbus_intr(pcf->iicbus, INTR_ERROR, &error);
40438781Snsouch
40538781Snsouch		goto error;
40638781Snsouch	}
40738781Snsouch
40838781Snsouch	do {
40938781Snsouch		status = PCF_GET_S1(pcf);
41038781Snsouch
41138781Snsouch		switch(pcf->pcf_slave_mode) {
41238781Snsouch
41338781Snsouch		case SLAVE_TRANSMITTER:
41438781Snsouch			if (status & LRB) {
41538781Snsouch				/* ack interrupt line */
41638781Snsouch				dummy_write(pcf);
41738781Snsouch
41838781Snsouch				/* no ack, don't send anymore */
41938781Snsouch				pcf->pcf_slave_mode = SLAVE_RECEIVER;
42038781Snsouch
42138781Snsouch				iicbus_intr(pcf->iicbus, INTR_NOACK, NULL);
42238781Snsouch				break;
42338781Snsouch			}
42438781Snsouch
42538781Snsouch			/* get data from upper code */
42638781Snsouch			iicbus_intr(pcf->iicbus, INTR_TRANSMIT, &data);
42738781Snsouch
42838781Snsouch			PCF_SET_S0(pcf, data);
42938781Snsouch			break;
43038781Snsouch
43138781Snsouch		case SLAVE_RECEIVER:
43238781Snsouch			if (status & AAS) {
43338781Snsouch				addr = PCF_GET_S0(pcf);
43438781Snsouch
43538781Snsouch				if (status & AD0)
43638781Snsouch					iicbus_intr(pcf->iicbus, INTR_GENERAL, &addr);
43738781Snsouch				else
43838781Snsouch					iicbus_intr(pcf->iicbus, INTR_START, &addr);
43938781Snsouch
44038781Snsouch				if (addr & LSB) {
44138781Snsouch					pcf->pcf_slave_mode = SLAVE_TRANSMITTER;
44238781Snsouch
44338781Snsouch					/* get the first char from upper code */
44438781Snsouch					iicbus_intr(pcf->iicbus, INTR_TRANSMIT, &data);
44538781Snsouch
44638781Snsouch					/* send first data byte */
44738781Snsouch					PCF_SET_S0(pcf, data);
44838781Snsouch				}
44938781Snsouch
45038781Snsouch				break;
45138781Snsouch			}
45238781Snsouch
45338781Snsouch			/* stop condition received? */
45438781Snsouch			if (status & STS) {
45538781Snsouch				/* ack interrupt line */
45638781Snsouch				dummy_read(pcf);
45738781Snsouch
45838781Snsouch				/* emulate intr stop condition */
45938781Snsouch				iicbus_intr(pcf->iicbus, INTR_STOP, NULL);
46038781Snsouch
46138781Snsouch			} else {
46238781Snsouch				/* get data, ack interrupt line */
46338781Snsouch				data = PCF_GET_S0(pcf);
46438781Snsouch
46538781Snsouch				/* deliver the character */
46638781Snsouch				iicbus_intr(pcf->iicbus, INTR_RECEIVE, &data);
46738781Snsouch			}
46838781Snsouch			break;
46938781Snsouch
47038781Snsouch		    default:
47187599Sobrien			panic("%s: unknown slave mode (%d)!", __func__,
47238781Snsouch				pcf->pcf_slave_mode);
47338781Snsouch		    }
47438781Snsouch
47538781Snsouch	} while ((PCF_GET_S1(pcf) & PIN) == 0);
47638781Snsouch
47738781Snsouch	return;
47838781Snsouch
47938781Snsoucherror:
48038781Snsouch	/* unknown event on bus...reset PCF */
48138781Snsouch	PCF_SET_S1(pcf, PIN|ES0|ENI|ACK);
48238781Snsouch
48338781Snsouch	pcf->pcf_slave_mode = SLAVE_RECEIVER;
48438781Snsouch
48538781Snsouch	return;
48638781Snsouch}
48738781Snsouch
48840784Snsouchstatic int pcf_rst_card(device_t pcfdev, u_char speed, u_char addr, u_char *oldaddr)
48938781Snsouch{
49038781Snsouch	struct pcf_softc *pcf = DEVTOSOFTC(pcfdev);
49138781Snsouch
49240784Snsouch	if (oldaddr)
49340784Snsouch		*oldaddr = pcf->pcf_addr;
49440784Snsouch
49538781Snsouch	/* retrieve own address from bus level */
49640784Snsouch	if (!addr)
49740784Snsouch		pcf->pcf_addr = PCF_DEFAULT_ADDR;
49840784Snsouch	else
49940784Snsouch		pcf->pcf_addr = addr;
50038781Snsouch
50138781Snsouch	PCF_SET_S1(pcf, PIN);				/* initialize S1 */
50238781Snsouch
50338781Snsouch	/* own address S'O<>0 */
50440784Snsouch	PCF_SET_S0(pcf, pcf->pcf_addr >> 1);
50538781Snsouch
50638781Snsouch	/* select clock register */
50738781Snsouch	PCF_SET_S1(pcf, PIN|ES1);
50838781Snsouch
50938781Snsouch	/* select bus speed : 18=90kb, 19=45kb, 1A=11kb, 1B=1.5kb */
51038781Snsouch	switch (speed) {
51138781Snsouch	case IIC_SLOW:
51238781Snsouch		PCF_SET_S0(pcf,  0x1b);
51338781Snsouch		break;
51438781Snsouch
51538781Snsouch	case IIC_FAST:
51638781Snsouch		PCF_SET_S0(pcf,  0x19);
51738781Snsouch		break;
51838781Snsouch
51938781Snsouch	case IIC_UNKNOWN:
52038781Snsouch	case IIC_FASTEST:
52138781Snsouch	default:
52238781Snsouch		PCF_SET_S0(pcf,  0x18);
52338781Snsouch		break;
52438781Snsouch	}
52538781Snsouch
52638781Snsouch	/* set bus on, ack=yes, INT=yes */
52738781Snsouch	PCF_SET_S1(pcf, PIN|ES0|ENI|ACK);
52838781Snsouch
52938781Snsouch	pcf->pcf_slave_mode = SLAVE_RECEIVER;
53038781Snsouch
53138781Snsouch	return (0);
53238781Snsouch}
53338781Snsouch
53438781Snsouchstatic int
53540784Snsouchpcf_write(device_t pcfdev, char *buf, int len, int *sent, int timeout /* us */)
53638781Snsouch{
53738781Snsouch	struct pcf_softc *pcf = DEVTOSOFTC(pcfdev);
53838781Snsouch	int bytes, error = 0;
53938781Snsouch
54038781Snsouch#ifdef PCFDEBUG
54138781Snsouch	printf("pcf%d: >> writing %d bytes\n", device_get_unit(pcfdev), len);
54238781Snsouch#endif
54338781Snsouch
54438781Snsouch	bytes = 0;
54538781Snsouch	while (len) {
54638781Snsouch
54738781Snsouch		PCF_SET_S0(pcf, *buf++);
54838781Snsouch
54940784Snsouch		/* wait for the byte to be send */
55038781Snsouch		if ((error = pcf_wait_byte(pcf)))
55138781Snsouch			goto error;
55238781Snsouch
55340784Snsouch		/* check if ack received */
55440784Snsouch		if (pcf_noack(pcf, timeout)) {
55538781Snsouch			error = IIC_ENOACK;
55638781Snsouch			goto error;
55738781Snsouch		}
55838781Snsouch
55938781Snsouch		len --;
56038781Snsouch		bytes ++;
56138781Snsouch	}
56238781Snsouch
56338781Snsoucherror:
56438781Snsouch	*sent = bytes;
56538781Snsouch
56638781Snsouch#ifdef PCFDEBUG
56738781Snsouch	printf("pcf%d: >> %d bytes written (%d)\n",
56838781Snsouch		device_get_unit(pcfdev), bytes, error);
56938781Snsouch#endif
57038781Snsouch
57138781Snsouch	return (error);
57238781Snsouch}
57338781Snsouch
57438781Snsouchstatic int
57540784Snsouchpcf_read(device_t pcfdev, char *buf, int len, int *read, int last,
57640784Snsouch							int delay /* us */)
57738781Snsouch{
57838781Snsouch	struct pcf_softc *pcf = DEVTOSOFTC(pcfdev);
57938781Snsouch	int bytes, error = 0;
58038781Snsouch
58138781Snsouch#ifdef PCFDEBUG
58238781Snsouch	printf("pcf%d: << reading %d bytes\n", device_get_unit(pcfdev), len);
58338781Snsouch#endif
58438781Snsouch
58538781Snsouch	/* trig the bus to get the first data byte in S0 */
58638781Snsouch	if (len) {
58740784Snsouch		if (len == 1 && last)
58838781Snsouch			/* just one byte to read */
58938781Snsouch			PCF_SET_S1(pcf, ES0);		/* no ack */
59038781Snsouch
59138781Snsouch		dummy_read(pcf);
59238781Snsouch	}
59338781Snsouch
59438781Snsouch	bytes = 0;
59538781Snsouch	while (len) {
59638781Snsouch
59740784Snsouch		/* XXX delay needed here */
59840784Snsouch
59940784Snsouch		/* wait for trigged byte */
60038781Snsouch		if ((error = pcf_wait_byte(pcf))) {
60138781Snsouch			pcf_stop(pcfdev);
60238781Snsouch			goto error;
60338781Snsouch		}
60438781Snsouch
60540784Snsouch		if (len == 1 && last)
60640784Snsouch			/* ok, last data byte already in S0, no I2C activity
60740784Snsouch			 * on next PCF_GET_S0() */
60838781Snsouch			pcf_stop(pcfdev);
60938781Snsouch
61040784Snsouch		else if (len == 2 && last)
61140784Snsouch			/* next trigged byte with no ack */
61240784Snsouch			PCF_SET_S1(pcf, ES0);
61338781Snsouch
61440784Snsouch		/* receive byte, trig next byte */
61540784Snsouch		*buf++ = PCF_GET_S0(pcf);
61638781Snsouch
61738781Snsouch		len --;
61838781Snsouch		bytes ++;
61938781Snsouch	};
62038781Snsouch
62138781Snsoucherror:
62238781Snsouch	*read = bytes;
62338781Snsouch
62438781Snsouch#ifdef PCFDEBUG
62538781Snsouch	printf("pcf%d: << %d bytes read (%d)\n",
62638781Snsouch		device_get_unit(pcfdev), bytes, error);
62738781Snsouch#endif
62838781Snsouch
62938781Snsouch	return (error);
63038781Snsouch}
63138781Snsouch
63255939SnsouchDRIVER_MODULE(pcf, isa, pcf_driver, pcf_devclass, 0, 0);
633