xref: /freebsd/sys/dev/pcf/pcf.c (revision 9d5abbddbf03791c777fed86500976ea3dd19245)
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  *
26c3aac50fSPeter Wemm  * $FreeBSD$
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 
350f210c92SNicolas Souchu #include <machine/bus.h>
360f210c92SNicolas Souchu #include <machine/resource.h>
370f210c92SNicolas Souchu #include <sys/rman.h>
380f210c92SNicolas Souchu 
390f210c92SNicolas Souchu #include <isa/isareg.h>
400f210c92SNicolas Souchu #include <isa/isavar.h>
414e190a62SNicolas Souchu 
424e190a62SNicolas Souchu #include <i386/isa/isa_device.h>
434e190a62SNicolas Souchu 
444e190a62SNicolas Souchu #include <dev/iicbus/iiconf.h>
454e190a62SNicolas Souchu #include "iicbus_if.h"
464e190a62SNicolas Souchu 
470f210c92SNicolas Souchu #define IO_PCFSIZE	2
480f210c92SNicolas Souchu 
49af548787SNicolas Souchu #define TIMEOUT	9999					/* XXX */
504e190a62SNicolas Souchu 
514e190a62SNicolas Souchu /* Status bits of S1 register (read only) */
524e190a62SNicolas Souchu #define nBB	0x01		/* busy when low set/reset by STOP/START*/
534e190a62SNicolas Souchu #define LAB	0x02		/* lost arbitration bit in multi-master mode */
544e190a62SNicolas Souchu #define AAS	0x04		/* addressed as slave */
554e190a62SNicolas Souchu #define LRB	0x08		/* last received byte when not AAS */
564e190a62SNicolas Souchu #define AD0	0x08		/* general call received when AAS */
574e190a62SNicolas Souchu #define BER	0x10		/* bus error, misplaced START or STOP */
584e190a62SNicolas Souchu #define STS	0x20		/* STOP detected in slave receiver mode */
594e190a62SNicolas Souchu #define PIN	0x80		/* pending interrupt not (r/w) */
604e190a62SNicolas Souchu 
614e190a62SNicolas Souchu /* Control bits of S1 register (write only) */
624e190a62SNicolas Souchu #define ACK	0x01
634e190a62SNicolas Souchu #define STO	0x02
644e190a62SNicolas Souchu #define STA	0x04
654e190a62SNicolas Souchu #define ENI	0x08
664e190a62SNicolas Souchu #define ES2	0x10
674e190a62SNicolas Souchu #define ES1	0x20
684e190a62SNicolas Souchu #define ES0	0x40
694e190a62SNicolas Souchu 
704e190a62SNicolas Souchu #define BUFSIZE 2048
714e190a62SNicolas Souchu 
724e190a62SNicolas Souchu #define SLAVE_TRANSMITTER	0x1
734e190a62SNicolas Souchu #define SLAVE_RECEIVER		0x2
744e190a62SNicolas Souchu 
75af548787SNicolas Souchu #define PCF_DEFAULT_ADDR	0xaa
76af548787SNicolas Souchu 
774e190a62SNicolas Souchu struct pcf_softc {
784e190a62SNicolas Souchu 
794e190a62SNicolas Souchu 	int pcf_base;			/* isa port */
800f210c92SNicolas Souchu 	int pcf_flags;
81af548787SNicolas Souchu 	u_char pcf_addr;		/* interface I2C address */
824e190a62SNicolas Souchu 
834e190a62SNicolas Souchu 	int pcf_slave_mode;		/* receiver or transmitter */
84af548787SNicolas Souchu 	int pcf_started;		/* 1 if start condition sent */
854e190a62SNicolas Souchu 
864e190a62SNicolas Souchu 	device_t iicbus;		/* the corresponding iicbus */
874e190a62SNicolas Souchu 
880f210c92SNicolas Souchu   	int rid_irq, rid_ioport;
890f210c92SNicolas Souchu 	struct resource *res_irq, *res_ioport;
900f210c92SNicolas Souchu 	void *intr_cookie;
914e190a62SNicolas Souchu };
924e190a62SNicolas Souchu 
934e190a62SNicolas Souchu static int pcf_probe(device_t);
944e190a62SNicolas Souchu static int pcf_attach(device_t);
950f210c92SNicolas Souchu static void pcfintr(void *arg);
960f210c92SNicolas Souchu 
9715317dd8SMatthew N. Dodd static int pcf_print_child(device_t, device_t);
984e190a62SNicolas Souchu 
99af548787SNicolas Souchu static int pcf_repeated_start(device_t, u_char, int);
100af548787SNicolas Souchu static int pcf_start(device_t, u_char, int);
1014e190a62SNicolas Souchu static int pcf_stop(device_t);
102e6b14871SPeter Wemm static int pcf_write(device_t, char *, int, int *, int);
103e6b14871SPeter Wemm static int pcf_read(device_t, char *, int, int *, int, int);
104e6b14871SPeter Wemm static int pcf_rst_card(device_t, u_char, u_char, u_char *);
1054e190a62SNicolas Souchu 
1064e190a62SNicolas Souchu static device_method_t pcf_methods[] = {
1074e190a62SNicolas Souchu 	/* device interface */
1084e190a62SNicolas Souchu 	DEVMETHOD(device_probe,		pcf_probe),
1094e190a62SNicolas Souchu 	DEVMETHOD(device_attach,	pcf_attach),
1104e190a62SNicolas Souchu 
1114e190a62SNicolas Souchu 	/* bus interface */
1124e190a62SNicolas Souchu 	DEVMETHOD(bus_print_child,	pcf_print_child),
1134e190a62SNicolas Souchu 
1144e190a62SNicolas Souchu 	/* iicbus interface */
115af548787SNicolas Souchu 	DEVMETHOD(iicbus_callback,	iicbus_null_callback),
1164e190a62SNicolas Souchu 	DEVMETHOD(iicbus_repeated_start, pcf_repeated_start),
1174e190a62SNicolas Souchu 	DEVMETHOD(iicbus_start,		pcf_start),
1184e190a62SNicolas Souchu 	DEVMETHOD(iicbus_stop,		pcf_stop),
1194e190a62SNicolas Souchu 	DEVMETHOD(iicbus_write,		pcf_write),
1204e190a62SNicolas Souchu 	DEVMETHOD(iicbus_read,		pcf_read),
1214e190a62SNicolas Souchu 	DEVMETHOD(iicbus_reset,		pcf_rst_card),
1224e190a62SNicolas Souchu 
1234e190a62SNicolas Souchu 	{ 0, 0 }
1244e190a62SNicolas Souchu };
1254e190a62SNicolas Souchu 
1264e190a62SNicolas Souchu static driver_t pcf_driver = {
1274e190a62SNicolas Souchu 	"pcf",
1284e190a62SNicolas Souchu 	pcf_methods,
1294e190a62SNicolas Souchu 	sizeof(struct pcf_softc),
1304e190a62SNicolas Souchu };
1314e190a62SNicolas Souchu 
1324e190a62SNicolas Souchu static devclass_t pcf_devclass;
1334e190a62SNicolas Souchu 
1344e190a62SNicolas Souchu #define DEVTOSOFTC(dev) ((struct pcf_softc *)device_get_softc(dev))
1354e190a62SNicolas Souchu 
1364e190a62SNicolas Souchu static int
1374e190a62SNicolas Souchu pcf_probe(device_t pcfdev)
1384e190a62SNicolas Souchu {
1390f210c92SNicolas Souchu 	struct pcf_softc *pcf = DEVTOSOFTC(pcfdev);
1400f210c92SNicolas Souchu 	device_t parent = device_get_parent(pcfdev);
141af548787SNicolas Souchu 
1420f210c92SNicolas Souchu 	device_set_desc(pcfdev, "PCF8584 I2C bus controller");
1430f210c92SNicolas Souchu 
1440f210c92SNicolas Souchu 	pcf = DEVTOSOFTC(pcfdev);
1450f210c92SNicolas Souchu 	bzero(pcf, sizeof(struct pcf_softc));
1460f210c92SNicolas Souchu 
1470f210c92SNicolas Souchu 	pcf->rid_irq = pcf->rid_ioport = 0;
1480f210c92SNicolas Souchu 	pcf->res_irq = pcf->res_ioport = 0;
1490f210c92SNicolas Souchu 
1500f210c92SNicolas Souchu 	/* IO port is mandatory */
1510f210c92SNicolas Souchu 	pcf->res_ioport = bus_alloc_resource(pcfdev, SYS_RES_IOPORT,
1520f210c92SNicolas Souchu 					     &pcf->rid_ioport, 0ul, ~0ul,
1530f210c92SNicolas Souchu 					     IO_PCFSIZE, RF_ACTIVE);
1540f210c92SNicolas Souchu 	if (pcf->res_ioport == 0) {
1550f210c92SNicolas Souchu 		device_printf(pcfdev, "cannot reserve I/O port range\n");
1560f210c92SNicolas Souchu 		goto error;
1570f210c92SNicolas Souchu 	}
1580f210c92SNicolas Souchu 	BUS_READ_IVAR(parent, pcfdev, ISA_IVAR_PORT, &pcf->pcf_base);
1590f210c92SNicolas Souchu 
1600f210c92SNicolas Souchu 	pcf->pcf_flags = device_get_flags(pcfdev);
1610f210c92SNicolas Souchu 
1620f210c92SNicolas Souchu 	if (!(pcf->pcf_flags & IIC_POLLED)) {
1630f210c92SNicolas Souchu 		pcf->res_irq = bus_alloc_resource(pcfdev, SYS_RES_IRQ, &pcf->rid_irq,
1640f210c92SNicolas Souchu 						  0ul, ~0ul, 1, RF_ACTIVE);
1650f210c92SNicolas Souchu 		if (pcf->res_irq == 0) {
1660f210c92SNicolas Souchu 			device_printf(pcfdev, "can't reserve irq, polled mode.\n");
1670f210c92SNicolas Souchu 			pcf->pcf_flags |= IIC_POLLED;
1680f210c92SNicolas Souchu 		}
1690f210c92SNicolas Souchu 	}
170af548787SNicolas Souchu 
171af548787SNicolas Souchu 	/* reset the chip */
172af548787SNicolas Souchu 	pcf_rst_card(pcfdev, IIC_FASTEST, PCF_DEFAULT_ADDR, NULL);
1734e190a62SNicolas Souchu 
1744e190a62SNicolas Souchu 	return (0);
1750f210c92SNicolas Souchu error:
1760f210c92SNicolas Souchu 	if (pcf->res_ioport != 0) {
1770f210c92SNicolas Souchu 		bus_deactivate_resource(pcfdev, SYS_RES_IOPORT, pcf->rid_ioport,
1780f210c92SNicolas Souchu 					pcf->res_ioport);
1790f210c92SNicolas Souchu 		bus_release_resource(pcfdev, SYS_RES_IOPORT, pcf->rid_ioport,
1800f210c92SNicolas Souchu 				     pcf->res_ioport);
1810f210c92SNicolas Souchu 	}
1820f210c92SNicolas Souchu 	return (ENXIO);
1834e190a62SNicolas Souchu }
1844e190a62SNicolas Souchu 
1854e190a62SNicolas Souchu static int
1864e190a62SNicolas Souchu pcf_attach(device_t pcfdev)
1874e190a62SNicolas Souchu {
1880f210c92SNicolas Souchu 	struct pcf_softc *pcf = DEVTOSOFTC(pcfdev);
1890f210c92SNicolas Souchu 	device_t parent = device_get_parent(pcfdev);
1900f210c92SNicolas Souchu 	int error = 0;
1910f210c92SNicolas Souchu 
1920f210c92SNicolas Souchu 	if (pcf->res_irq) {
1930f210c92SNicolas Souchu 		/* default to the tty mask for registration */	/* XXX */
1940f210c92SNicolas Souchu 		error = BUS_SETUP_INTR(parent, pcfdev, pcf->res_irq, INTR_TYPE_NET,
1950f210c92SNicolas Souchu 					    pcfintr, pcfdev, &pcf->intr_cookie);
1960f210c92SNicolas Souchu 		if (error)
1970f210c92SNicolas Souchu 			return (error);
1980f210c92SNicolas Souchu 	}
1994e190a62SNicolas Souchu 
200ea4122d2SNicolas Souchu 	pcf->iicbus = device_add_child(pcfdev, "iicbus", -1);
201af548787SNicolas Souchu 
2024e190a62SNicolas Souchu 	/* probe and attach the iicbus */
203ea4122d2SNicolas Souchu 	bus_generic_attach(pcfdev);
2044e190a62SNicolas Souchu 
2054e190a62SNicolas Souchu 	return (0);
2064e190a62SNicolas Souchu }
2074e190a62SNicolas Souchu 
20815317dd8SMatthew N. Dodd static int
2094e190a62SNicolas Souchu pcf_print_child(device_t bus, device_t dev)
2104e190a62SNicolas Souchu {
211af548787SNicolas Souchu 	struct pcf_softc *pcf = (struct pcf_softc *)device_get_softc(bus);
21215317dd8SMatthew N. Dodd 	int retval = 0;
213af548787SNicolas Souchu 
21415317dd8SMatthew N. Dodd 	retval += bus_print_child_header(bus, dev);
21515317dd8SMatthew N. Dodd 	retval += printf(" on %s addr 0x%x\n", device_get_nameunit(bus),
21615317dd8SMatthew N. Dodd 			 (int)pcf->pcf_addr);
2174e190a62SNicolas Souchu 
21815317dd8SMatthew N. Dodd 	return (retval);
2194e190a62SNicolas Souchu }
2204e190a62SNicolas Souchu 
2214e190a62SNicolas Souchu /*
2224e190a62SNicolas Souchu  * PCF8584 datasheet : when operate at 8 MHz or more, a minimun time of
2234e190a62SNicolas Souchu  * 6 clocks cycles must be left between two consecutives access
2244e190a62SNicolas Souchu  */
2254e190a62SNicolas Souchu #define pcf_nops()	DELAY(10)
2264e190a62SNicolas Souchu 
2274e190a62SNicolas Souchu #define dummy_read(pcf)		PCF_GET_S0(pcf)
2284e190a62SNicolas Souchu #define dummy_write(pcf)	PCF_SET_S0(pcf, 0)
2294e190a62SNicolas Souchu 
2304e190a62SNicolas Souchu /*
2314e190a62SNicolas Souchu  * Specific register access to PCF8584
2324e190a62SNicolas Souchu  */
2334e190a62SNicolas Souchu static void PCF_SET_S0(struct pcf_softc *pcf, int data)
2344e190a62SNicolas Souchu {
2354e190a62SNicolas Souchu 	outb(pcf->pcf_base, data);
2364e190a62SNicolas Souchu 	pcf_nops();
2374e190a62SNicolas Souchu }
2384e190a62SNicolas Souchu 
2394e190a62SNicolas Souchu static void PCF_SET_S1(struct pcf_softc *pcf, int data)
2404e190a62SNicolas Souchu {
2414e190a62SNicolas Souchu 	outb(pcf->pcf_base+1, data);
2424e190a62SNicolas Souchu 	pcf_nops();
2434e190a62SNicolas Souchu }
2444e190a62SNicolas Souchu 
2454e190a62SNicolas Souchu static char PCF_GET_S0(struct pcf_softc *pcf)
2464e190a62SNicolas Souchu {
2474e190a62SNicolas Souchu 	char data;
2484e190a62SNicolas Souchu 
2494e190a62SNicolas Souchu 	data = inb(pcf->pcf_base);
2504e190a62SNicolas Souchu 	pcf_nops();
2514e190a62SNicolas Souchu 
2524e190a62SNicolas Souchu 	return (data);
2534e190a62SNicolas Souchu }
2544e190a62SNicolas Souchu 
2554e190a62SNicolas Souchu static char PCF_GET_S1(struct pcf_softc *pcf)
2564e190a62SNicolas Souchu {
2574e190a62SNicolas Souchu 	char data;
2584e190a62SNicolas Souchu 
2594e190a62SNicolas Souchu 	data = inb(pcf->pcf_base+1);
2604e190a62SNicolas Souchu 	pcf_nops();
2614e190a62SNicolas Souchu 
2624e190a62SNicolas Souchu 	return (data);
2634e190a62SNicolas Souchu }
2644e190a62SNicolas Souchu 
2654e190a62SNicolas Souchu /*
2664e190a62SNicolas Souchu  * Polling mode for master operations wait for a new
2674e190a62SNicolas Souchu  * byte incomming or outgoing
2684e190a62SNicolas Souchu  */
2694e190a62SNicolas Souchu static int pcf_wait_byte(struct pcf_softc *pcf)
2704e190a62SNicolas Souchu {
2714e190a62SNicolas Souchu 	int counter = TIMEOUT;
2724e190a62SNicolas Souchu 
2734e190a62SNicolas Souchu 	while (counter--) {
2744e190a62SNicolas Souchu 
2754e190a62SNicolas Souchu 		if ((PCF_GET_S1(pcf) & PIN) == 0)
2764e190a62SNicolas Souchu 			return (0);
2774e190a62SNicolas Souchu 	}
2784e190a62SNicolas Souchu 
2794e190a62SNicolas Souchu 	return (IIC_ETIMEOUT);
2804e190a62SNicolas Souchu }
2814e190a62SNicolas Souchu 
2824e190a62SNicolas Souchu static int pcf_stop(device_t pcfdev)
2834e190a62SNicolas Souchu {
2844e190a62SNicolas Souchu 	struct pcf_softc *pcf = DEVTOSOFTC(pcfdev);
2854e190a62SNicolas Souchu 
286af548787SNicolas Souchu 	/*
287af548787SNicolas Souchu 	 * Send STOP condition iff the START condition was previously sent.
2889d5abbddSJens Schweikhardt 	 * STOP is sent only once even if an iicbus_stop() is called after
289af548787SNicolas Souchu 	 * an iicbus_read()... see pcf_read(): the pcf needs to send the stop
290af548787SNicolas Souchu 	 * before the last char is read.
291af548787SNicolas Souchu 	 */
292af548787SNicolas Souchu 	if (pcf->pcf_started) {
2934e190a62SNicolas Souchu 		/* set stop condition and enable IT */
2944e190a62SNicolas Souchu 		PCF_SET_S1(pcf, PIN|ES0|ENI|STO|ACK);
2954e190a62SNicolas Souchu 
296af548787SNicolas Souchu 		pcf->pcf_started = 0;
297af548787SNicolas Souchu 	}
298af548787SNicolas Souchu 
2994e190a62SNicolas Souchu 	return (0);
3004e190a62SNicolas Souchu }
3014e190a62SNicolas Souchu 
302af548787SNicolas Souchu 
303af548787SNicolas Souchu static int pcf_noack(struct pcf_softc *pcf, int timeout)
304af548787SNicolas Souchu {
305af548787SNicolas Souchu 	int noack;
306af548787SNicolas Souchu 	int k = timeout/10;
307af548787SNicolas Souchu 
308af548787SNicolas Souchu 	do {
309af548787SNicolas Souchu 		noack = PCF_GET_S1(pcf) & LRB;
310af548787SNicolas Souchu 		if (!noack)
311af548787SNicolas Souchu 			break;
312af548787SNicolas Souchu 		DELAY(10);				/* XXX wait 10 us */
313af548787SNicolas Souchu 	} while (k--);
314af548787SNicolas Souchu 
315af548787SNicolas Souchu 	return (noack);
316af548787SNicolas Souchu }
317af548787SNicolas Souchu 
318af548787SNicolas Souchu static int pcf_repeated_start(device_t pcfdev, u_char slave, int timeout)
3194e190a62SNicolas Souchu {
3204e190a62SNicolas Souchu 	struct pcf_softc *pcf = DEVTOSOFTC(pcfdev);
3214e190a62SNicolas Souchu 	int error = 0;
3224e190a62SNicolas Souchu 
3234e190a62SNicolas Souchu 	/* repeated start */
3244e190a62SNicolas Souchu 	PCF_SET_S1(pcf, ES0|STA|STO|ACK);
3254e190a62SNicolas Souchu 
3264e190a62SNicolas Souchu 	/* set slave address to PCF. Last bit (LSB) must be set correctly
3274e190a62SNicolas Souchu 	 * according to transfer direction */
3284e190a62SNicolas Souchu 	PCF_SET_S0(pcf, slave);
3294e190a62SNicolas Souchu 
3304e190a62SNicolas Souchu 	/* wait for address sent, polling */
3314e190a62SNicolas Souchu 	if ((error = pcf_wait_byte(pcf)))
3324e190a62SNicolas Souchu 		goto error;
3334e190a62SNicolas Souchu 
334af548787SNicolas Souchu 	/* check for ack */
335af548787SNicolas Souchu 	if (pcf_noack(pcf, timeout)) {
3364e190a62SNicolas Souchu 		error = IIC_ENOACK;
3374e190a62SNicolas Souchu 		goto error;
3384e190a62SNicolas Souchu 	}
3394e190a62SNicolas Souchu 
3404e190a62SNicolas Souchu 	return (0);
3414e190a62SNicolas Souchu 
3424e190a62SNicolas Souchu error:
3434e190a62SNicolas Souchu 	pcf_stop(pcfdev);
3444e190a62SNicolas Souchu 	return (error);
3454e190a62SNicolas Souchu }
3464e190a62SNicolas Souchu 
347af548787SNicolas Souchu static int pcf_start(device_t pcfdev, u_char slave, int timeout)
3484e190a62SNicolas Souchu {
3494e190a62SNicolas Souchu 	struct pcf_softc *pcf = DEVTOSOFTC(pcfdev);
3504e190a62SNicolas Souchu 	int error = 0;
3514e190a62SNicolas Souchu 
352f79cfdd9SPeter Wemm 	if ((PCF_GET_S1(pcf) & nBB) == 0)
3534e190a62SNicolas Souchu 		return (IIC_EBUSBSY);
3544e190a62SNicolas Souchu 
3554e190a62SNicolas Souchu 	/* set slave address to PCF. Last bit (LSB) must be set correctly
3564e190a62SNicolas Souchu 	 * according to transfer direction */
3574e190a62SNicolas Souchu 	PCF_SET_S0(pcf, slave);
3584e190a62SNicolas Souchu 
3594e190a62SNicolas Souchu 	/* START only */
3604e190a62SNicolas Souchu 	PCF_SET_S1(pcf, PIN|ES0|STA|ACK);
3614e190a62SNicolas Souchu 
362af548787SNicolas Souchu 	pcf->pcf_started = 1;
363af548787SNicolas Souchu 
3644e190a62SNicolas Souchu 	/* wait for address sent, polling */
3654e190a62SNicolas Souchu 	if ((error = pcf_wait_byte(pcf)))
3664e190a62SNicolas Souchu 		goto error;
3674e190a62SNicolas Souchu 
368af548787SNicolas Souchu 	/* check for ACK */
369af548787SNicolas Souchu 	if (pcf_noack(pcf, timeout)) {
3704e190a62SNicolas Souchu 		error = IIC_ENOACK;
3714e190a62SNicolas Souchu 		goto error;
3724e190a62SNicolas Souchu 	}
3734e190a62SNicolas Souchu 
3744e190a62SNicolas Souchu 	return (0);
3754e190a62SNicolas Souchu 
3764e190a62SNicolas Souchu error:
3774e190a62SNicolas Souchu 	pcf_stop(pcfdev);
3784e190a62SNicolas Souchu 	return (error);
3794e190a62SNicolas Souchu }
3804e190a62SNicolas Souchu 
381fe310de8SBruce Evans static void
3820f210c92SNicolas Souchu pcfintr(void *arg)
3834e190a62SNicolas Souchu {
3840f210c92SNicolas Souchu 	device_t pcfdev = (device_t)arg;
3850f210c92SNicolas Souchu 	struct pcf_softc *pcf = DEVTOSOFTC(pcfdev);
3864e190a62SNicolas Souchu 
3874e190a62SNicolas Souchu 	char data, status, addr;
3884e190a62SNicolas Souchu 	char error = 0;
3894e190a62SNicolas Souchu 
3904e190a62SNicolas Souchu 	status = PCF_GET_S1(pcf);
3914e190a62SNicolas Souchu 
3924e190a62SNicolas Souchu 	if (status & PIN) {
3930f210c92SNicolas Souchu 		device_printf(pcfdev, "spurious interrupt, status=0x%x\n", status & 0xff);
3944e190a62SNicolas Souchu 
3954e190a62SNicolas Souchu 		goto error;
3964e190a62SNicolas Souchu 	}
3974e190a62SNicolas Souchu 
3984e190a62SNicolas Souchu 	if (status & LAB)
3990f210c92SNicolas Souchu 		device_printf(pcfdev, "bus arbitration lost!\n");
4004e190a62SNicolas Souchu 
4014e190a62SNicolas Souchu 	if (status & BER) {
4024e190a62SNicolas Souchu 		error = IIC_EBUSERR;
4034e190a62SNicolas Souchu 		iicbus_intr(pcf->iicbus, INTR_ERROR, &error);
4044e190a62SNicolas Souchu 
4054e190a62SNicolas Souchu 		goto error;
4064e190a62SNicolas Souchu 	}
4074e190a62SNicolas Souchu 
4084e190a62SNicolas Souchu 	do {
4094e190a62SNicolas Souchu 		status = PCF_GET_S1(pcf);
4104e190a62SNicolas Souchu 
4114e190a62SNicolas Souchu 		switch(pcf->pcf_slave_mode) {
4124e190a62SNicolas Souchu 
4134e190a62SNicolas Souchu 		case SLAVE_TRANSMITTER:
4144e190a62SNicolas Souchu 			if (status & LRB) {
4154e190a62SNicolas Souchu 				/* ack interrupt line */
4164e190a62SNicolas Souchu 				dummy_write(pcf);
4174e190a62SNicolas Souchu 
4184e190a62SNicolas Souchu 				/* no ack, don't send anymore */
4194e190a62SNicolas Souchu 				pcf->pcf_slave_mode = SLAVE_RECEIVER;
4204e190a62SNicolas Souchu 
4214e190a62SNicolas Souchu 				iicbus_intr(pcf->iicbus, INTR_NOACK, NULL);
4224e190a62SNicolas Souchu 				break;
4234e190a62SNicolas Souchu 			}
4244e190a62SNicolas Souchu 
4254e190a62SNicolas Souchu 			/* get data from upper code */
4264e190a62SNicolas Souchu 			iicbus_intr(pcf->iicbus, INTR_TRANSMIT, &data);
4274e190a62SNicolas Souchu 
4284e190a62SNicolas Souchu 			PCF_SET_S0(pcf, data);
4294e190a62SNicolas Souchu 			break;
4304e190a62SNicolas Souchu 
4314e190a62SNicolas Souchu 		case SLAVE_RECEIVER:
4324e190a62SNicolas Souchu 			if (status & AAS) {
4334e190a62SNicolas Souchu 				addr = PCF_GET_S0(pcf);
4344e190a62SNicolas Souchu 
4354e190a62SNicolas Souchu 				if (status & AD0)
4364e190a62SNicolas Souchu 					iicbus_intr(pcf->iicbus, INTR_GENERAL, &addr);
4374e190a62SNicolas Souchu 				else
4384e190a62SNicolas Souchu 					iicbus_intr(pcf->iicbus, INTR_START, &addr);
4394e190a62SNicolas Souchu 
4404e190a62SNicolas Souchu 				if (addr & LSB) {
4414e190a62SNicolas Souchu 					pcf->pcf_slave_mode = SLAVE_TRANSMITTER;
4424e190a62SNicolas Souchu 
4434e190a62SNicolas Souchu 					/* get the first char from upper code */
4444e190a62SNicolas Souchu 					iicbus_intr(pcf->iicbus, INTR_TRANSMIT, &data);
4454e190a62SNicolas Souchu 
4464e190a62SNicolas Souchu 					/* send first data byte */
4474e190a62SNicolas Souchu 					PCF_SET_S0(pcf, data);
4484e190a62SNicolas Souchu 				}
4494e190a62SNicolas Souchu 
4504e190a62SNicolas Souchu 				break;
4514e190a62SNicolas Souchu 			}
4524e190a62SNicolas Souchu 
4534e190a62SNicolas Souchu 			/* stop condition received? */
4544e190a62SNicolas Souchu 			if (status & STS) {
4554e190a62SNicolas Souchu 				/* ack interrupt line */
4564e190a62SNicolas Souchu 				dummy_read(pcf);
4574e190a62SNicolas Souchu 
4584e190a62SNicolas Souchu 				/* emulate intr stop condition */
4594e190a62SNicolas Souchu 				iicbus_intr(pcf->iicbus, INTR_STOP, NULL);
4604e190a62SNicolas Souchu 
4614e190a62SNicolas Souchu 			} else {
4624e190a62SNicolas Souchu 				/* get data, ack interrupt line */
4634e190a62SNicolas Souchu 				data = PCF_GET_S0(pcf);
4644e190a62SNicolas Souchu 
4654e190a62SNicolas Souchu 				/* deliver the character */
4664e190a62SNicolas Souchu 				iicbus_intr(pcf->iicbus, INTR_RECEIVE, &data);
4674e190a62SNicolas Souchu 			}
4684e190a62SNicolas Souchu 			break;
4694e190a62SNicolas Souchu 
4704e190a62SNicolas Souchu 		    default:
4716e551fb6SDavid E. O'Brien 			panic("%s: unknown slave mode (%d)!", __func__,
4724e190a62SNicolas Souchu 				pcf->pcf_slave_mode);
4734e190a62SNicolas Souchu 		    }
4744e190a62SNicolas Souchu 
4754e190a62SNicolas Souchu 	} while ((PCF_GET_S1(pcf) & PIN) == 0);
4764e190a62SNicolas Souchu 
4774e190a62SNicolas Souchu 	return;
4784e190a62SNicolas Souchu 
4794e190a62SNicolas Souchu error:
4804e190a62SNicolas Souchu 	/* unknown event on bus...reset PCF */
4814e190a62SNicolas Souchu 	PCF_SET_S1(pcf, PIN|ES0|ENI|ACK);
4824e190a62SNicolas Souchu 
4834e190a62SNicolas Souchu 	pcf->pcf_slave_mode = SLAVE_RECEIVER;
4844e190a62SNicolas Souchu 
4854e190a62SNicolas Souchu 	return;
4864e190a62SNicolas Souchu }
4874e190a62SNicolas Souchu 
488af548787SNicolas Souchu static int pcf_rst_card(device_t pcfdev, u_char speed, u_char addr, u_char *oldaddr)
4894e190a62SNicolas Souchu {
4904e190a62SNicolas Souchu 	struct pcf_softc *pcf = DEVTOSOFTC(pcfdev);
491af548787SNicolas Souchu 
492af548787SNicolas Souchu 	if (oldaddr)
493af548787SNicolas Souchu 		*oldaddr = pcf->pcf_addr;
4944e190a62SNicolas Souchu 
4954e190a62SNicolas Souchu 	/* retrieve own address from bus level */
496af548787SNicolas Souchu 	if (!addr)
497af548787SNicolas Souchu 		pcf->pcf_addr = PCF_DEFAULT_ADDR;
498af548787SNicolas Souchu 	else
499af548787SNicolas Souchu 		pcf->pcf_addr = addr;
5004e190a62SNicolas Souchu 
5014e190a62SNicolas Souchu 	PCF_SET_S1(pcf, PIN);				/* initialize S1 */
5024e190a62SNicolas Souchu 
5034e190a62SNicolas Souchu 	/* own address S'O<>0 */
504af548787SNicolas Souchu 	PCF_SET_S0(pcf, pcf->pcf_addr >> 1);
5054e190a62SNicolas Souchu 
5064e190a62SNicolas Souchu 	/* select clock register */
5074e190a62SNicolas Souchu 	PCF_SET_S1(pcf, PIN|ES1);
5084e190a62SNicolas Souchu 
5094e190a62SNicolas Souchu 	/* select bus speed : 18=90kb, 19=45kb, 1A=11kb, 1B=1.5kb */
5104e190a62SNicolas Souchu 	switch (speed) {
5114e190a62SNicolas Souchu 	case IIC_SLOW:
5124e190a62SNicolas Souchu 		PCF_SET_S0(pcf,  0x1b);
5134e190a62SNicolas Souchu 		break;
5144e190a62SNicolas Souchu 
5154e190a62SNicolas Souchu 	case IIC_FAST:
5164e190a62SNicolas Souchu 		PCF_SET_S0(pcf,  0x19);
5174e190a62SNicolas Souchu 		break;
5184e190a62SNicolas Souchu 
5194e190a62SNicolas Souchu 	case IIC_UNKNOWN:
5204e190a62SNicolas Souchu 	case IIC_FASTEST:
5214e190a62SNicolas Souchu 	default:
5224e190a62SNicolas Souchu 		PCF_SET_S0(pcf,  0x18);
5234e190a62SNicolas Souchu 		break;
5244e190a62SNicolas Souchu 	}
5254e190a62SNicolas Souchu 
5264e190a62SNicolas Souchu 	/* set bus on, ack=yes, INT=yes */
5274e190a62SNicolas Souchu 	PCF_SET_S1(pcf, PIN|ES0|ENI|ACK);
5284e190a62SNicolas Souchu 
5294e190a62SNicolas Souchu 	pcf->pcf_slave_mode = SLAVE_RECEIVER;
5304e190a62SNicolas Souchu 
5314e190a62SNicolas Souchu 	return (0);
5324e190a62SNicolas Souchu }
5334e190a62SNicolas Souchu 
5344e190a62SNicolas Souchu static int
535af548787SNicolas Souchu pcf_write(device_t pcfdev, char *buf, int len, int *sent, int timeout /* us */)
5364e190a62SNicolas Souchu {
5374e190a62SNicolas Souchu 	struct pcf_softc *pcf = DEVTOSOFTC(pcfdev);
5384e190a62SNicolas Souchu 	int bytes, error = 0;
5394e190a62SNicolas Souchu 
5404e190a62SNicolas Souchu #ifdef PCFDEBUG
5414e190a62SNicolas Souchu 	printf("pcf%d: >> writing %d bytes\n", device_get_unit(pcfdev), len);
5424e190a62SNicolas Souchu #endif
5434e190a62SNicolas Souchu 
5444e190a62SNicolas Souchu 	bytes = 0;
5454e190a62SNicolas Souchu 	while (len) {
5464e190a62SNicolas Souchu 
5474e190a62SNicolas Souchu 		PCF_SET_S0(pcf, *buf++);
5484e190a62SNicolas Souchu 
549af548787SNicolas Souchu 		/* wait for the byte to be send */
5504e190a62SNicolas Souchu 		if ((error = pcf_wait_byte(pcf)))
5514e190a62SNicolas Souchu 			goto error;
5524e190a62SNicolas Souchu 
553af548787SNicolas Souchu 		/* check if ack received */
554af548787SNicolas Souchu 		if (pcf_noack(pcf, timeout)) {
5554e190a62SNicolas Souchu 			error = IIC_ENOACK;
5564e190a62SNicolas Souchu 			goto error;
5574e190a62SNicolas Souchu 		}
5584e190a62SNicolas Souchu 
5594e190a62SNicolas Souchu 		len --;
5604e190a62SNicolas Souchu 		bytes ++;
5614e190a62SNicolas Souchu 	}
5624e190a62SNicolas Souchu 
5634e190a62SNicolas Souchu error:
5644e190a62SNicolas Souchu 	*sent = bytes;
5654e190a62SNicolas Souchu 
5664e190a62SNicolas Souchu #ifdef PCFDEBUG
5674e190a62SNicolas Souchu 	printf("pcf%d: >> %d bytes written (%d)\n",
5684e190a62SNicolas Souchu 		device_get_unit(pcfdev), bytes, error);
5694e190a62SNicolas Souchu #endif
5704e190a62SNicolas Souchu 
5714e190a62SNicolas Souchu 	return (error);
5724e190a62SNicolas Souchu }
5734e190a62SNicolas Souchu 
5744e190a62SNicolas Souchu static int
575af548787SNicolas Souchu pcf_read(device_t pcfdev, char *buf, int len, int *read, int last,
576af548787SNicolas Souchu 							int delay /* us */)
5774e190a62SNicolas Souchu {
5784e190a62SNicolas Souchu 	struct pcf_softc *pcf = DEVTOSOFTC(pcfdev);
5794e190a62SNicolas Souchu 	int bytes, error = 0;
5804e190a62SNicolas Souchu 
5814e190a62SNicolas Souchu #ifdef PCFDEBUG
5824e190a62SNicolas Souchu 	printf("pcf%d: << reading %d bytes\n", device_get_unit(pcfdev), len);
5834e190a62SNicolas Souchu #endif
5844e190a62SNicolas Souchu 
5854e190a62SNicolas Souchu 	/* trig the bus to get the first data byte in S0 */
5864e190a62SNicolas Souchu 	if (len) {
587af548787SNicolas Souchu 		if (len == 1 && last)
5884e190a62SNicolas Souchu 			/* just one byte to read */
5894e190a62SNicolas Souchu 			PCF_SET_S1(pcf, ES0);		/* no ack */
5904e190a62SNicolas Souchu 
5914e190a62SNicolas Souchu 		dummy_read(pcf);
5924e190a62SNicolas Souchu 	}
5934e190a62SNicolas Souchu 
5944e190a62SNicolas Souchu 	bytes = 0;
5954e190a62SNicolas Souchu 	while (len) {
5964e190a62SNicolas Souchu 
597af548787SNicolas Souchu 		/* XXX delay needed here */
598af548787SNicolas Souchu 
599af548787SNicolas Souchu 		/* wait for trigged byte */
6004e190a62SNicolas Souchu 		if ((error = pcf_wait_byte(pcf))) {
6014e190a62SNicolas Souchu 			pcf_stop(pcfdev);
6024e190a62SNicolas Souchu 			goto error;
6034e190a62SNicolas Souchu 		}
6044e190a62SNicolas Souchu 
605af548787SNicolas Souchu 		if (len == 1 && last)
606af548787SNicolas Souchu 			/* ok, last data byte already in S0, no I2C activity
607af548787SNicolas Souchu 			 * on next PCF_GET_S0() */
6084e190a62SNicolas Souchu 			pcf_stop(pcfdev);
6094e190a62SNicolas Souchu 
610af548787SNicolas Souchu 		else if (len == 2 && last)
6114e190a62SNicolas Souchu 			/* next trigged byte with no ack */
6124e190a62SNicolas Souchu 			PCF_SET_S1(pcf, ES0);
6134e190a62SNicolas Souchu 
614af548787SNicolas Souchu 		/* receive byte, trig next byte */
6154e190a62SNicolas Souchu 		*buf++ = PCF_GET_S0(pcf);
6164e190a62SNicolas Souchu 
6174e190a62SNicolas Souchu 		len --;
6184e190a62SNicolas Souchu 		bytes ++;
6194e190a62SNicolas Souchu 	};
6204e190a62SNicolas Souchu 
6214e190a62SNicolas Souchu error:
6224e190a62SNicolas Souchu 	*read = bytes;
6234e190a62SNicolas Souchu 
6244e190a62SNicolas Souchu #ifdef PCFDEBUG
6254e190a62SNicolas Souchu 	printf("pcf%d: << %d bytes read (%d)\n",
6264e190a62SNicolas Souchu 		device_get_unit(pcfdev), bytes, error);
6274e190a62SNicolas Souchu #endif
6284e190a62SNicolas Souchu 
6294e190a62SNicolas Souchu 	return (error);
6304e190a62SNicolas Souchu }
6314e190a62SNicolas Souchu 
6320f210c92SNicolas Souchu DRIVER_MODULE(pcf, isa, pcf_driver, pcf_devclass, 0, 0);
633