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