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