140782Snsouch/*-
293023Snsouch * Copyright (c) 1998, 2001 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
27119418Sobrien#include <sys/cdefs.h>
28119418Sobrien__FBSDID("$FreeBSD: releng/11.0/sys/dev/iicbus/iicbb.c 289657 2015-10-20 19:52:59Z dumbbell $");
29119418Sobrien
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 */
4540782Snsouch
46261844Sloos#include "opt_platform.h"
47261844Sloos
4840782Snsouch#include <sys/param.h>
4940782Snsouch#include <sys/kernel.h>
5040782Snsouch#include <sys/systm.h>
5140782Snsouch#include <sys/module.h>
5240782Snsouch#include <sys/bus.h>
5340782Snsouch#include <sys/uio.h>
5440782Snsouch
55261844Sloos#ifdef FDT
56261844Sloos#include <dev/ofw/ofw_bus.h>
57261844Sloos#include <dev/ofw/ofw_bus_subr.h>
58261844Sloos#include <dev/fdt/fdt_common.h>
59261844Sloos#endif
6040782Snsouch
6140782Snsouch#include <dev/iicbus/iiconf.h>
6240782Snsouch#include <dev/iicbus/iicbus.h>
6340782Snsouch
6440782Snsouch#include <dev/smbus/smbconf.h>
6540782Snsouch
6640782Snsouch#include "iicbus_if.h"
6740782Snsouch#include "iicbb_if.h"
6840782Snsouch
6940782Snsouchstruct iicbb_softc {
7093023Snsouch	device_t iicbus;
71228728Sadrian	int udelay;		/* signal toggle delay in usec */
7240782Snsouch};
7340782Snsouch
7440782Snsouchstatic int iicbb_attach(device_t);
75164900Simpstatic void iicbb_child_detached(device_t, device_t);
7693023Snsouchstatic int iicbb_detach(device_t);
7749195Smdoddstatic int iicbb_print_child(device_t, device_t);
78164900Simpstatic int iicbb_probe(device_t);
7940782Snsouch
8040782Snsouchstatic int iicbb_callback(device_t, int, caddr_t);
8140782Snsouchstatic int iicbb_start(device_t, u_char, int);
8240782Snsouchstatic int iicbb_stop(device_t);
83188461Simpstatic int iicbb_write(device_t, const char *, int, int *, int);
8440782Snsouchstatic int iicbb_read(device_t, char *, int, int *, int, int);
8540782Snsouchstatic int iicbb_reset(device_t, u_char, u_char, u_char *);
86232365Skanstatic int iicbb_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs);
87261844Sloos#ifdef FDT
88261844Sloosstatic phandle_t iicbb_get_node(device_t, device_t);
89261844Sloos#endif
9040782Snsouch
9140782Snsouchstatic device_method_t iicbb_methods[] = {
9240782Snsouch	/* device interface */
9340782Snsouch	DEVMETHOD(device_probe,		iicbb_probe),
9440782Snsouch	DEVMETHOD(device_attach,	iicbb_attach),
9593023Snsouch	DEVMETHOD(device_detach,	iicbb_detach),
9640782Snsouch
9740782Snsouch	/* bus interface */
98164900Simp	DEVMETHOD(bus_child_detached,	iicbb_child_detached),
9940782Snsouch	DEVMETHOD(bus_print_child,	iicbb_print_child),
10040782Snsouch
10140782Snsouch	/* iicbus interface */
10240782Snsouch	DEVMETHOD(iicbus_callback,	iicbb_callback),
10340782Snsouch	DEVMETHOD(iicbus_start,		iicbb_start),
10440782Snsouch	DEVMETHOD(iicbus_repeated_start, iicbb_start),
10540782Snsouch	DEVMETHOD(iicbus_stop,		iicbb_stop),
10640782Snsouch	DEVMETHOD(iicbus_write,		iicbb_write),
10740782Snsouch	DEVMETHOD(iicbus_read,		iicbb_read),
10840782Snsouch	DEVMETHOD(iicbus_reset,		iicbb_reset),
109232365Skan	DEVMETHOD(iicbus_transfer,	iicbb_transfer),
11040782Snsouch
111261844Sloos#ifdef FDT
112261844Sloos	/* ofw_bus interface */
113261844Sloos	DEVMETHOD(ofw_bus_get_node,	iicbb_get_node),
114261844Sloos#endif
115261844Sloos
11640782Snsouch	{ 0, 0 }
11740782Snsouch};
11840782Snsouch
119116559Sjmgdriver_t iicbb_driver = {
12040782Snsouch	"iicbb",
12140782Snsouch	iicbb_methods,
12240782Snsouch	sizeof(struct iicbb_softc),
12340782Snsouch};
12440782Snsouch
125116559Sjmgdevclass_t iicbb_devclass;
12640782Snsouch
127161516Simpstatic int
128161516Simpiicbb_probe(device_t dev)
12940782Snsouch{
13093023Snsouch	device_set_desc(dev, "I2C bit-banging driver");
13140782Snsouch
13240782Snsouch	return (0);
13340782Snsouch}
13440782Snsouch
135161516Simpstatic int
136161516Simpiicbb_attach(device_t dev)
13740782Snsouch{
13893023Snsouch	struct iicbb_softc *sc = (struct iicbb_softc *)device_get_softc(dev);
13993023Snsouch
14093023Snsouch	sc->iicbus = device_add_child(dev, "iicbus", -1);
14193023Snsouch	if (!sc->iicbus)
14293023Snsouch		return (ENXIO);
143228728Sadrian	sc->udelay = 10;		/* 10 uS default */
14493023Snsouch	bus_generic_attach(dev);
14593023Snsouch
14640782Snsouch	return (0);
14740782Snsouch}
14840782Snsouch
149161516Simpstatic int
150161516Simpiicbb_detach(device_t dev)
15193023Snsouch{
15293023Snsouch
153164900Simp	bus_generic_detach(dev);
154289657Sdumbbell	device_delete_children(dev);
15593023Snsouch
15693023Snsouch	return (0);
15793023Snsouch}
15893023Snsouch
159261844Sloos#ifdef FDT
160261844Sloosstatic phandle_t
161261844Sloosiicbb_get_node(device_t bus, device_t dev)
162261844Sloos{
163261844Sloos
164261844Sloos	/* We only have one child, the I2C bus, which needs our own node. */
165261844Sloos	return (ofw_bus_get_node(bus));
166261844Sloos}
167261844Sloos#endif
168261844Sloos
169164900Simpstatic void
170164900Simpiicbb_child_detached( device_t dev, device_t child )
171164900Simp{
172164900Simp	struct iicbb_softc *sc = (struct iicbb_softc *)device_get_softc(dev);
173164900Simp
174164900Simp	if (child == sc->iicbus)
175164900Simp		sc->iicbus = NULL;
176164900Simp}
177164900Simp
17849195Smdoddstatic int
17940782Snsouchiicbb_print_child(device_t bus, device_t dev)
18040782Snsouch{
18140782Snsouch	int error;
18249195Smdodd	int retval = 0;
18340782Snsouch	u_char oldaddr;
18440782Snsouch
18549195Smdodd	retval += bus_print_child_header(bus, dev);
18640782Snsouch	/* retrieve the interface I2C address */
18740914Snsouch	error = IICBB_RESET(device_get_parent(bus), IIC_FASTEST, 0, &oldaddr);
18840782Snsouch	if (error == IIC_ENOADDR) {
18949195Smdodd		retval += printf(" on %s master-only\n",
19049195Smdodd				 device_get_nameunit(bus));
19140782Snsouch	} else {
19240782Snsouch		/* restore the address */
19340914Snsouch		IICBB_RESET(device_get_parent(bus), IIC_FASTEST, oldaddr, NULL);
19440782Snsouch
19549195Smdodd		retval += printf(" on %s addr 0x%x\n",
19649195Smdodd				 device_get_nameunit(bus), oldaddr & 0xff);
19740782Snsouch	}
19840782Snsouch
19949195Smdodd	return (retval);
20040782Snsouch}
20140782Snsouch
202228728Sadrian#define I2C_SETSDA(sc,dev,val) do {			\
20393023Snsouch	IICBB_SETSDA(device_get_parent(dev), val);	\
204228728Sadrian	DELAY(sc->udelay);				\
20593023Snsouch	} while (0)
20640782Snsouch
20793023Snsouch#define I2C_SETSCL(dev,val) do {			\
20893023Snsouch	iicbb_setscl(dev, val, 100);			\
20993023Snsouch	} while (0)
21093023Snsouch
211228728Sadrian#define I2C_SET(sc,dev,ctrl,data) do {			\
21293023Snsouch	I2C_SETSCL(dev, ctrl);				\
213228728Sadrian	I2C_SETSDA(sc, dev, data);			\
21493023Snsouch	} while (0)
21593023Snsouch
21693023Snsouch#define I2C_GETSDA(dev) (IICBB_GETSDA(device_get_parent(dev)))
21793023Snsouch
21893023Snsouch#define I2C_GETSCL(dev) (IICBB_GETSCL(device_get_parent(dev)))
21993023Snsouch
22040782Snsouchstatic int i2c_debug = 0;
22193023Snsouch#define I2C_DEBUG(x)	do {					\
22293023Snsouch				if (i2c_debug) (x);		\
22393023Snsouch			} while (0)
22440782Snsouch
22593023Snsouch#define I2C_LOG(format,args...)	do {				\
22693023Snsouch					printf(format, args);	\
22793023Snsouch				} while (0)
22893023Snsouch
229161516Simpstatic void
230161516Simpiicbb_setscl(device_t dev, int val, int timeout)
23140782Snsouch{
232228728Sadrian	struct iicbb_softc *sc = device_get_softc(dev);
23393023Snsouch	int k = 0;
23493023Snsouch
23593023Snsouch	IICBB_SETSCL(device_get_parent(dev), val);
236228728Sadrian	DELAY(sc->udelay);
23793023Snsouch
23893023Snsouch	while (val && !I2C_GETSCL(dev) && k++ < timeout) {
23993023Snsouch		IICBB_SETSCL(device_get_parent(dev), val);
240228728Sadrian		DELAY(sc->udelay);
24193023Snsouch	}
242228728Sadrian
24393023Snsouch	return;
24493023Snsouch}
24593023Snsouch
246161516Simpstatic void
247161516Simpiicbb_one(device_t dev, int timeout)
24893023Snsouch{
249228728Sadrian	struct iicbb_softc *sc = device_get_softc(dev);
250228728Sadrian
251228728Sadrian	I2C_SET(sc,dev,0,1);
252228728Sadrian	I2C_SET(sc,dev,1,1);
253228728Sadrian	I2C_SET(sc,dev,0,1);
25440782Snsouch	return;
25540782Snsouch}
25640782Snsouch
257161516Simpstatic void
258161516Simpiicbb_zero(device_t dev, int timeout)
25940782Snsouch{
260228728Sadrian	struct iicbb_softc *sc = device_get_softc(dev);
261228728Sadrian
262228728Sadrian	I2C_SET(sc,dev,0,0);
263228728Sadrian	I2C_SET(sc,dev,1,0);
264228728Sadrian	I2C_SET(sc,dev,0,0);
26540782Snsouch	return;
26640782Snsouch}
26740782Snsouch
26840782Snsouch/*
26940782Snsouch * Waiting for ACKNOWLEDGE.
27040782Snsouch *
27140782Snsouch * When a chip is being addressed or has received data it will issue an
27240782Snsouch * ACKNOWLEDGE pulse. Therefore the MASTER must release the DATA line
27340782Snsouch * (set it to high level) and then release the CLOCK line.
27440782Snsouch * Now it must wait for the SLAVE to pull the DATA line low.
27540782Snsouch * Actually on the bus this looks like a START condition so nothing happens
27640782Snsouch * because of the fact that the IC's that have not been addressed are doing
27740782Snsouch * nothing.
27840782Snsouch *
27940782Snsouch * When the SLAVE has pulled this line low the MASTER will take the CLOCK
28040782Snsouch * line low and then the SLAVE will release the SDA (data) line.
28140782Snsouch */
282161516Simpstatic int
283161516Simpiicbb_ack(device_t dev, int timeout)
28440782Snsouch{
285228728Sadrian	struct iicbb_softc *sc = device_get_softc(dev);
28640782Snsouch	int noack;
28793023Snsouch	int k = 0;
288228728Sadrian
289228728Sadrian	I2C_SET(sc,dev,0,1);
290228728Sadrian	I2C_SET(sc,dev,1,1);
29140782Snsouch	do {
29293023Snsouch		noack = I2C_GETSDA(dev);
29340782Snsouch		if (!noack)
29440782Snsouch			break;
295228728Sadrian		DELAY(1);
296228728Sadrian		k++;
29793023Snsouch	} while (k < timeout);
29840782Snsouch
299228728Sadrian	I2C_SET(sc,dev,0,1);
30040782Snsouch	I2C_DEBUG(printf("%c ",noack?'-':'+'));
30140782Snsouch
30240782Snsouch	return (noack);
30340782Snsouch}
30440782Snsouch
305161516Simpstatic void
306161516Simpiicbb_sendbyte(device_t dev, u_char data, int timeout)
30740782Snsouch{
30840782Snsouch	int i;
30940782Snsouch
31093023Snsouch	for (i=7; i>=0; i--) {
31193023Snsouch		if (data&(1<<i)) {
31293023Snsouch			iicbb_one(dev, timeout);
31393023Snsouch		} else {
31493023Snsouch			iicbb_zero(dev, timeout);
31593023Snsouch		}
31693023Snsouch	}
31740782Snsouch	I2C_DEBUG(printf("w%02x",(int)data));
31840782Snsouch	return;
31940782Snsouch}
32040782Snsouch
321161516Simpstatic u_char
322161516Simpiicbb_readbyte(device_t dev, int last, int timeout)
32340782Snsouch{
324228728Sadrian	struct iicbb_softc *sc = device_get_softc(dev);
32540782Snsouch	int i;
32640782Snsouch	unsigned char data=0;
327228728Sadrian
328228728Sadrian	I2C_SET(sc,dev,0,1);
32940782Snsouch	for (i=7; i>=0; i--)
33040782Snsouch	{
331228728Sadrian		I2C_SET(sc,dev,1,1);
33293023Snsouch		if (I2C_GETSDA(dev))
33340782Snsouch			data |= (1<<i);
334228728Sadrian		I2C_SET(sc,dev,0,1);
33540782Snsouch	}
33693023Snsouch	if (last) {
33793023Snsouch		iicbb_one(dev, timeout);
33893023Snsouch	} else {
33993023Snsouch		iicbb_zero(dev, timeout);
34093023Snsouch	}
34140782Snsouch	I2C_DEBUG(printf("r%02x%c ",(int)data,last?'-':'+'));
34240782Snsouch	return data;
34340782Snsouch}
34440782Snsouch
345161516Simpstatic int
346161516Simpiicbb_callback(device_t dev, int index, caddr_t data)
34740782Snsouch{
34840782Snsouch	return (IICBB_CALLBACK(device_get_parent(dev), index, data));
34940782Snsouch}
35040782Snsouch
351161516Simpstatic int
352161516Simpiicbb_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr)
35340782Snsouch{
35440782Snsouch	return (IICBB_RESET(device_get_parent(dev), speed, addr, oldaddr));
35540782Snsouch}
35640782Snsouch
357161516Simpstatic int
358161516Simpiicbb_start(device_t dev, u_char slave, int timeout)
35940782Snsouch{
360228728Sadrian	struct iicbb_softc *sc = device_get_softc(dev);
36140782Snsouch	int error;
36240782Snsouch
36340782Snsouch	I2C_DEBUG(printf("<"));
36440782Snsouch
365228728Sadrian	I2C_SET(sc,dev,1,1);
366228728Sadrian	I2C_SET(sc,dev,1,0);
367228728Sadrian	I2C_SET(sc,dev,0,0);
36840782Snsouch
36940782Snsouch	/* send address */
37093023Snsouch	iicbb_sendbyte(dev, slave, timeout);
37140782Snsouch
37240782Snsouch	/* check for ack */
37340782Snsouch	if (iicbb_ack(dev, timeout)) {
37440782Snsouch		error = IIC_ENOACK;
37540782Snsouch		goto error;
37640782Snsouch	}
37740782Snsouch
37840782Snsouch	return(0);
37940782Snsouch
38040782Snsoucherror:
38140782Snsouch	iicbb_stop(dev);
38240782Snsouch	return (error);
38340782Snsouch}
38440782Snsouch
385161516Simpstatic int
386161516Simpiicbb_stop(device_t dev)
38740782Snsouch{
388228728Sadrian	struct iicbb_softc *sc = device_get_softc(dev);
389228728Sadrian
390228728Sadrian	I2C_SET(sc,dev,0,0);
391228728Sadrian	I2C_SET(sc,dev,1,0);
392228728Sadrian	I2C_SET(sc,dev,1,1);
39340782Snsouch	I2C_DEBUG(printf(">"));
394228728Sadrian	I2C_DEBUG(printf("\n"));
39540782Snsouch	return (0);
39640782Snsouch}
39740782Snsouch
398161516Simpstatic int
399188461Simpiicbb_write(device_t dev, const char *buf, int len, int *sent, int timeout)
40040782Snsouch{
40140782Snsouch	int bytes, error = 0;
40240782Snsouch
40340782Snsouch	bytes = 0;
40440782Snsouch	while (len) {
40540782Snsouch		/* send byte */
40693023Snsouch		iicbb_sendbyte(dev,(u_char)*buf++, timeout);
40740782Snsouch
40840782Snsouch		/* check for ack */
40940782Snsouch		if (iicbb_ack(dev, timeout)) {
41040782Snsouch			error = IIC_ENOACK;
41140782Snsouch			goto error;
41240782Snsouch		}
41340782Snsouch		bytes ++;
41440782Snsouch		len --;
41540782Snsouch	}
41640782Snsouch
41740782Snsoucherror:
41840782Snsouch	*sent = bytes;
41940782Snsouch	return (error);
42040782Snsouch}
42140782Snsouch
422161516Simpstatic int
423161516Simpiicbb_read(device_t dev, char * buf, int len, int *read, int last, int delay)
42440782Snsouch{
42540782Snsouch	int bytes;
42640782Snsouch
42740782Snsouch	bytes = 0;
42840782Snsouch	while (len) {
42940782Snsouch		/* XXX should insert delay here */
43093023Snsouch		*buf++ = (char)iicbb_readbyte(dev, (len == 1) ? last : 0, delay);
43140782Snsouch
43240782Snsouch		bytes ++;
43340782Snsouch		len --;
43440782Snsouch	}
43540782Snsouch
43640782Snsouch	*read = bytes;
43740782Snsouch	return (0);
43840782Snsouch}
43940782Snsouch
440232365Skanstatic int
441232365Skaniicbb_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs)
442232365Skan{
443232365Skan	int error;
444232365Skan
445232365Skan	error = IICBB_PRE_XFER(device_get_parent(dev));
446232365Skan	if (error)
447232365Skan		return (error);
448232365Skan
449232365Skan	error = iicbus_transfer_gen(dev, msgs, nmsgs);
450232365Skan
451232365Skan	IICBB_POST_XFER(device_get_parent(dev));
452232365Skan	return (error);
453232365Skan}
454232365Skan
455181303SjhbDRIVER_MODULE(iicbus, iicbb, iicbus_driver, iicbus_devclass, 0, 0);
45693023Snsouch
45793023SnsouchMODULE_DEPEND(iicbb, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER);
45893023SnsouchMODULE_VERSION(iicbb, IICBB_MODVER);
459