iicbb.c revision 40782
140782Snsouch/*-
240782Snsouch * Copyright (c) 1998 Nicolas Souchu
340782Snsouch * All rights reserved.
440782Snsouch *
540782Snsouch * Redistribution and use in source and binary forms, with or without
640782Snsouch * modification, are permitted provided that the following conditions
740782Snsouch * are met:
840782Snsouch * 1. Redistributions of source code must retain the above copyright
940782Snsouch *    notice, this list of conditions and the following disclaimer.
1040782Snsouch * 2. Redistributions in binary form must reproduce the above copyright
1140782Snsouch *    notice, this list of conditions and the following disclaimer in the
1240782Snsouch *    documentation and/or other materials provided with the distribution.
1340782Snsouch *
1440782Snsouch * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1540782Snsouch * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1640782Snsouch * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1740782Snsouch * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1840782Snsouch * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1940782Snsouch * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2040782Snsouch * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2140782Snsouch * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2240782Snsouch * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2340782Snsouch * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2440782Snsouch * SUCH DAMAGE.
2540782Snsouch *
2640782Snsouch *	$Id$
2740782Snsouch *
2840782Snsouch */
2940782Snsouch
3040782Snsouch/*
3140782Snsouch * Generic I2C bit-banging code
3240782Snsouch *
3340782Snsouch * Example:
3440782Snsouch *
3540782Snsouch *	iicbus
3640782Snsouch *	 /  \
3740782Snsouch *    iicbb pcf
3840782Snsouch *     |  \
3940782Snsouch *   bti2c lpbb
4040782Snsouch *
4140782Snsouch * From Linux I2C generic interface
4240782Snsouch * (c) 1998 Gerd Knorr <kraxel@cs.tu-berlin.de>
4340782Snsouch *
4440782Snsouch * TODO: port Peter's generic bit-banging code <dufault@hda.com>
4540782Snsouch */
4640782Snsouch
4740782Snsouch#include <sys/param.h>
4840782Snsouch#include <sys/kernel.h>
4940782Snsouch#include <sys/systm.h>
5040782Snsouch#include <sys/module.h>
5140782Snsouch#include <sys/bus.h>
5240782Snsouch#include <sys/conf.h>
5340782Snsouch#include <sys/buf.h>
5440782Snsouch#include <sys/uio.h>
5540782Snsouch#include <sys/malloc.h>
5640782Snsouch
5740782Snsouch#include <machine/clock.h>
5840782Snsouch
5940782Snsouch#include <dev/iicbus/iiconf.h>
6040782Snsouch#include <dev/iicbus/iicbus.h>
6140782Snsouch
6240782Snsouch#include <dev/smbus/smbconf.h>
6340782Snsouch
6440782Snsouch#include "iicbus_if.h"
6540782Snsouch#include "iicbb_if.h"
6640782Snsouch
6740782Snsouchstruct iicbb_softc {
6840782Snsouch	int dummy;
6940782Snsouch};
7040782Snsouch
7140782Snsouchstatic int iicbb_probe(device_t);
7240782Snsouchstatic int iicbb_attach(device_t);
7340782Snsouchstatic void iicbb_print_child(device_t, device_t);
7440782Snsouch
7540782Snsouchstatic int iicbb_callback(device_t, int, caddr_t);
7640782Snsouchstatic int iicbb_start(device_t, u_char, int);
7740782Snsouchstatic int iicbb_stop(device_t);
7840782Snsouchstatic int iicbb_write(device_t, char *, int, int *, int);
7940782Snsouchstatic int iicbb_read(device_t, char *, int, int *, int, int);
8040782Snsouchstatic int iicbb_reset(device_t, u_char, u_char, u_char *);
8140782Snsouch
8240782Snsouchstatic device_method_t iicbb_methods[] = {
8340782Snsouch	/* device interface */
8440782Snsouch	DEVMETHOD(device_probe,		iicbb_probe),
8540782Snsouch	DEVMETHOD(device_attach,	iicbb_attach),
8640782Snsouch
8740782Snsouch	/* bus interface */
8840782Snsouch	DEVMETHOD(bus_print_child,	iicbb_print_child),
8940782Snsouch
9040782Snsouch	/* iicbus interface */
9140782Snsouch	DEVMETHOD(iicbus_callback,	iicbb_callback),
9240782Snsouch	DEVMETHOD(iicbus_start,		iicbb_start),
9340782Snsouch	DEVMETHOD(iicbus_repeated_start, iicbb_start),
9440782Snsouch	DEVMETHOD(iicbus_stop,		iicbb_stop),
9540782Snsouch	DEVMETHOD(iicbus_write,		iicbb_write),
9640782Snsouch	DEVMETHOD(iicbus_read,		iicbb_read),
9740782Snsouch	DEVMETHOD(iicbus_reset,		iicbb_reset),
9840782Snsouch
9940782Snsouch	{ 0, 0 }
10040782Snsouch};
10140782Snsouch
10240782Snsouchstatic driver_t iicbb_driver = {
10340782Snsouch	"iicbb",
10440782Snsouch	iicbb_methods,
10540782Snsouch	DRIVER_TYPE_MISC,
10640782Snsouch	sizeof(struct iicbb_softc),
10740782Snsouch};
10840782Snsouch
10940782Snsouchstatic devclass_t iicbb_devclass;
11040782Snsouch
11140782Snsouchstatic int iicbb_probe(device_t dev)
11240782Snsouch{
11340782Snsouch	device_set_desc(dev, "I2C generic bit-banging driver");
11440782Snsouch
11540782Snsouch	return (0);
11640782Snsouch}
11740782Snsouch
11840782Snsouchstatic int iicbb_attach(device_t dev)
11940782Snsouch{
12040782Snsouch	return (0);
12140782Snsouch}
12240782Snsouch
12340782Snsouchstatic void
12440782Snsouchiicbb_print_child(device_t bus, device_t dev)
12540782Snsouch{
12640782Snsouch	int error;
12740782Snsouch	u_char oldaddr;
12840782Snsouch
12940782Snsouch	/* retrieve the interface I2C address */
13040782Snsouch	error = IICBB_RESET(bus, IIC_FASTEST, 0, &oldaddr);
13140782Snsouch	if (error == IIC_ENOADDR) {
13240782Snsouch		printf(" on %s%d master-only", device_get_name(bus),
13340782Snsouch			device_get_unit(bus));
13440782Snsouch
13540782Snsouch	} else {
13640782Snsouch		/* restore the address */
13740782Snsouch		IICBB_RESET(bus, IIC_FASTEST, oldaddr, NULL);
13840782Snsouch
13940782Snsouch		printf(" on %s%d addr 0x%x", device_get_name(bus),
14040782Snsouch			device_get_unit(bus), oldaddr & 0xff);
14140782Snsouch	}
14240782Snsouch
14340782Snsouch	return;
14440782Snsouch}
14540782Snsouch
14640782Snsouch#define I2C_SET(dev,ctrl,data) \
14740782Snsouch	IICBB_SETLINES(device_get_parent(dev), ctrl, data)
14840782Snsouch
14940782Snsouch#define I2C_GET(dev) (IICBB_GETDATALINE(device_get_parent(dev)))
15040782Snsouch
15140782Snsouchstatic int i2c_debug = 0;
15240782Snsouch#define I2C_DEBUG(x) if (i2c_debug) (x)
15340782Snsouch
15440782Snsouchstatic void iicbb_one(device_t dev)
15540782Snsouch{
15640782Snsouch	I2C_SET(dev,0,1);
15740782Snsouch	I2C_SET(dev,1,1);
15840782Snsouch	I2C_SET(dev,0,1);
15940782Snsouch	return;
16040782Snsouch}
16140782Snsouch
16240782Snsouchstatic void iicbb_zero(device_t dev)
16340782Snsouch{
16440782Snsouch	I2C_SET(dev,0,0);
16540782Snsouch	I2C_SET(dev,1,0);
16640782Snsouch	I2C_SET(dev,0,0);
16740782Snsouch	return;
16840782Snsouch}
16940782Snsouch
17040782Snsouch/*
17140782Snsouch * Waiting for ACKNOWLEDGE.
17240782Snsouch *
17340782Snsouch * When a chip is being addressed or has received data it will issue an
17440782Snsouch * ACKNOWLEDGE pulse. Therefore the MASTER must release the DATA line
17540782Snsouch * (set it to high level) and then release the CLOCK line.
17640782Snsouch * Now it must wait for the SLAVE to pull the DATA line low.
17740782Snsouch * Actually on the bus this looks like a START condition so nothing happens
17840782Snsouch * because of the fact that the IC's that have not been addressed are doing
17940782Snsouch * nothing.
18040782Snsouch *
18140782Snsouch * When the SLAVE has pulled this line low the MASTER will take the CLOCK
18240782Snsouch * line low and then the SLAVE will release the SDA (data) line.
18340782Snsouch */
18440782Snsouchstatic int iicbb_ack(device_t dev, int timeout)
18540782Snsouch{
18640782Snsouch	int noack;
18740782Snsouch	int k = timeout/10;
18840782Snsouch
18940782Snsouch	I2C_SET(dev,0,1);
19040782Snsouch	I2C_SET(dev,1,1);
19140782Snsouch
19240782Snsouch	do {
19340782Snsouch		noack = I2C_GET(dev);
19440782Snsouch		if (!noack)
19540782Snsouch			break;
19640782Snsouch		DELAY(10);		/* XXX wait 10us */
19740782Snsouch	} while (k--);
19840782Snsouch
19940782Snsouch	I2C_SET(dev,0,1);
20040782Snsouch	I2C_DEBUG(printf("%c ",noack?'-':'+'));
20140782Snsouch
20240782Snsouch	return (noack);
20340782Snsouch}
20440782Snsouch
20540782Snsouchstatic void iicbb_sendbyte(device_t dev, u_char data)
20640782Snsouch{
20740782Snsouch	int i;
20840782Snsouch
20940782Snsouch	I2C_SET(dev,0,0);
21040782Snsouch	for (i=7; i>=0; i--)
21140782Snsouch		(data&(1<<i)) ? iicbb_one(dev) : iicbb_zero(dev);
21240782Snsouch	I2C_DEBUG(printf("w%02x",(int)data));
21340782Snsouch	return;
21440782Snsouch}
21540782Snsouch
21640782Snsouchstatic u_char iicbb_readbyte(device_t dev, int last)
21740782Snsouch{
21840782Snsouch	int i;
21940782Snsouch	unsigned char data=0;
22040782Snsouch
22140782Snsouch	I2C_SET(dev,0,1);
22240782Snsouch	for (i=7; i>=0; i--)
22340782Snsouch	{
22440782Snsouch		I2C_SET(dev,1,1);
22540782Snsouch		if (I2C_GET(dev))
22640782Snsouch			data |= (1<<i);
22740782Snsouch		I2C_SET(dev,0,1);
22840782Snsouch	}
22940782Snsouch	last ? iicbb_one(dev) : iicbb_zero(dev);
23040782Snsouch	I2C_DEBUG(printf("r%02x%c ",(int)data,last?'-':'+'));
23140782Snsouch	return data;
23240782Snsouch}
23340782Snsouch
23440782Snsouchstatic int iicbb_callback(device_t dev, int index, caddr_t data)
23540782Snsouch{
23640782Snsouch	return (IICBB_CALLBACK(device_get_parent(dev), index, data));
23740782Snsouch}
23840782Snsouch
23940782Snsouchstatic int iicbb_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr)
24040782Snsouch{
24140782Snsouch	return (IICBB_RESET(device_get_parent(dev), speed, addr, oldaddr));
24240782Snsouch}
24340782Snsouch
24440782Snsouchstatic int iicbb_start(device_t dev, u_char slave, int timeout)
24540782Snsouch{
24640782Snsouch	int error;
24740782Snsouch
24840782Snsouch	I2C_DEBUG(printf("<"));
24940782Snsouch
25040782Snsouch	I2C_SET(dev,0,1);
25140782Snsouch	I2C_SET(dev,1,1);
25240782Snsouch	I2C_SET(dev,1,0);
25340782Snsouch	I2C_SET(dev,0,0);
25440782Snsouch
25540782Snsouch	/* send address */
25640782Snsouch	iicbb_sendbyte(dev, slave);
25740782Snsouch
25840782Snsouch	/* check for ack */
25940782Snsouch	if (iicbb_ack(dev, timeout)) {
26040782Snsouch		error = IIC_ENOACK;
26140782Snsouch		goto error;
26240782Snsouch	}
26340782Snsouch
26440782Snsouch	return(0);
26540782Snsouch
26640782Snsoucherror:
26740782Snsouch	iicbb_stop(dev);
26840782Snsouch	return (error);
26940782Snsouch}
27040782Snsouch
27140782Snsouchstatic int iicbb_stop(device_t dev)
27240782Snsouch{
27340782Snsouch	I2C_SET(dev,0,0);
27440782Snsouch	I2C_SET(dev,1,0);
27540782Snsouch	I2C_SET(dev,1,1);
27640782Snsouch	I2C_DEBUG(printf(">"));
27740782Snsouch	return (0);
27840782Snsouch}
27940782Snsouch
28040782Snsouchstatic int iicbb_write(device_t dev, char * buf, int len, int *sent,
28140782Snsouch			int timeout)
28240782Snsouch{
28340782Snsouch	int bytes, error = 0;
28440782Snsouch
28540782Snsouch	bytes = 0;
28640782Snsouch	while (len) {
28740782Snsouch		/* send byte */
28840782Snsouch		iicbb_sendbyte(dev,(u_char)*buf++);
28940782Snsouch
29040782Snsouch		/* check for ack */
29140782Snsouch		if (iicbb_ack(dev, timeout)) {
29240782Snsouch			error = IIC_ENOACK;
29340782Snsouch			goto error;
29440782Snsouch		}
29540782Snsouch		bytes ++;
29640782Snsouch		len --;
29740782Snsouch	}
29840782Snsouch
29940782Snsoucherror:
30040782Snsouch	*sent = bytes;
30140782Snsouch	return (error);
30240782Snsouch}
30340782Snsouch
30440782Snsouchstatic int iicbb_read(device_t dev, char * buf, int len, int *read,
30540782Snsouch			int last, int delay)
30640782Snsouch{
30740782Snsouch	int bytes;
30840782Snsouch
30940782Snsouch	bytes = 0;
31040782Snsouch	while (len) {
31140782Snsouch		/* XXX should insert delay here */
31240782Snsouch		*buf++ = (char)iicbb_readbyte(dev, (len == 1) ? 1 : 0);
31340782Snsouch
31440782Snsouch		bytes ++;
31540782Snsouch		len --;
31640782Snsouch	}
31740782Snsouch
31840782Snsouch	*read = bytes;
31940782Snsouch	return (0);
32040782Snsouch}
32140782Snsouch
32240782SnsouchDRIVER_MODULE(iicbb, bti2c, iicbb_driver, iicbb_devclass, 0, 0);
32340782SnsouchDRIVER_MODULE(iicbb, lpbb, iicbb_driver, iicbb_devclass, 0, 0);
324