1 /*- 2 * Copyright (c) 1998 Nicolas Souchu 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD$ 27 * 28 */ 29 30 /* 31 * Generic I2C bit-banging code 32 * 33 * Example: 34 * 35 * iicbus 36 * / \ 37 * iicbb pcf 38 * | \ 39 * bti2c lpbb 40 * 41 * From Linux I2C generic interface 42 * (c) 1998 Gerd Knorr <kraxel@cs.tu-berlin.de> 43 * 44 * TODO: port Peter's generic bit-banging code <dufault@hda.com> 45 */ 46 47 #include <sys/param.h> 48 #include <sys/kernel.h> 49 #include <sys/systm.h> 50 #include <sys/module.h> 51 #include <sys/bus.h> 52 #include <sys/uio.h> 53 54 #include <machine/clock.h> 55 56 #include <dev/iicbus/iiconf.h> 57 #include <dev/iicbus/iicbus.h> 58 59 #include <dev/smbus/smbconf.h> 60 61 #include "iicbus_if.h" 62 #include "iicbb_if.h" 63 64 struct iicbb_softc { 65 int dummy; 66 }; 67 68 static int iicbb_probe(device_t); 69 static int iicbb_attach(device_t); 70 static int iicbb_print_child(device_t, device_t); 71 72 static int iicbb_callback(device_t, int, caddr_t); 73 static int iicbb_start(device_t, u_char, int); 74 static int iicbb_stop(device_t); 75 static int iicbb_write(device_t, char *, int, int *, int); 76 static int iicbb_read(device_t, char *, int, int *, int, int); 77 static int iicbb_reset(device_t, u_char, u_char, u_char *); 78 79 static device_method_t iicbb_methods[] = { 80 /* device interface */ 81 DEVMETHOD(device_probe, iicbb_probe), 82 DEVMETHOD(device_attach, iicbb_attach), 83 DEVMETHOD(device_detach, bus_generic_detach), 84 85 /* bus interface */ 86 DEVMETHOD(bus_print_child, iicbb_print_child), 87 88 /* iicbus interface */ 89 DEVMETHOD(iicbus_callback, iicbb_callback), 90 DEVMETHOD(iicbus_start, iicbb_start), 91 DEVMETHOD(iicbus_repeated_start, iicbb_start), 92 DEVMETHOD(iicbus_stop, iicbb_stop), 93 DEVMETHOD(iicbus_write, iicbb_write), 94 DEVMETHOD(iicbus_read, iicbb_read), 95 DEVMETHOD(iicbus_reset, iicbb_reset), 96 97 { 0, 0 } 98 }; 99 100 static driver_t iicbb_driver = { 101 "iicbb", 102 iicbb_methods, 103 sizeof(struct iicbb_softc), 104 }; 105 106 static devclass_t iicbb_devclass; 107 108 static int iicbb_probe(device_t dev) 109 { 110 device_set_desc(dev, "I2C generic bit-banging driver"); 111 112 return (0); 113 } 114 115 static int iicbb_attach(device_t dev) 116 { 117 return (0); 118 } 119 120 static int 121 iicbb_print_child(device_t bus, device_t dev) 122 { 123 int error; 124 int retval = 0; 125 u_char oldaddr; 126 127 retval += bus_print_child_header(bus, dev); 128 /* retrieve the interface I2C address */ 129 error = IICBB_RESET(device_get_parent(bus), IIC_FASTEST, 0, &oldaddr); 130 if (error == IIC_ENOADDR) { 131 retval += printf(" on %s master-only\n", 132 device_get_nameunit(bus)); 133 } else { 134 /* restore the address */ 135 IICBB_RESET(device_get_parent(bus), IIC_FASTEST, oldaddr, NULL); 136 137 retval += printf(" on %s addr 0x%x\n", 138 device_get_nameunit(bus), oldaddr & 0xff); 139 } 140 141 return (retval); 142 } 143 144 #define I2C_SET(dev,ctrl,data) \ 145 IICBB_SETLINES(device_get_parent(dev), ctrl, data) 146 147 #define I2C_GET(dev) (IICBB_GETDATALINE(device_get_parent(dev))) 148 149 static int i2c_debug = 0; 150 #define I2C_DEBUG(x) if (i2c_debug) (x) 151 152 static void iicbb_one(device_t dev) 153 { 154 I2C_SET(dev,0,1); 155 I2C_SET(dev,1,1); 156 I2C_SET(dev,0,1); 157 return; 158 } 159 160 static void iicbb_zero(device_t dev) 161 { 162 I2C_SET(dev,0,0); 163 I2C_SET(dev,1,0); 164 I2C_SET(dev,0,0); 165 return; 166 } 167 168 /* 169 * Waiting for ACKNOWLEDGE. 170 * 171 * When a chip is being addressed or has received data it will issue an 172 * ACKNOWLEDGE pulse. Therefore the MASTER must release the DATA line 173 * (set it to high level) and then release the CLOCK line. 174 * Now it must wait for the SLAVE to pull the DATA line low. 175 * Actually on the bus this looks like a START condition so nothing happens 176 * because of the fact that the IC's that have not been addressed are doing 177 * nothing. 178 * 179 * When the SLAVE has pulled this line low the MASTER will take the CLOCK 180 * line low and then the SLAVE will release the SDA (data) line. 181 */ 182 static int iicbb_ack(device_t dev, int timeout) 183 { 184 int noack; 185 int k = timeout/10; 186 187 I2C_SET(dev,0,1); 188 I2C_SET(dev,1,1); 189 190 do { 191 noack = I2C_GET(dev); 192 if (!noack) 193 break; 194 DELAY(10); /* XXX wait 10us */ 195 } while (k--); 196 197 I2C_SET(dev,0,1); 198 I2C_DEBUG(printf("%c ",noack?'-':'+')); 199 200 return (noack); 201 } 202 203 static void iicbb_sendbyte(device_t dev, u_char data) 204 { 205 int i; 206 207 I2C_SET(dev,0,0); 208 for (i=7; i>=0; i--) 209 (data&(1<<i)) ? iicbb_one(dev) : iicbb_zero(dev); 210 I2C_DEBUG(printf("w%02x",(int)data)); 211 return; 212 } 213 214 static u_char iicbb_readbyte(device_t dev, int last) 215 { 216 int i; 217 unsigned char data=0; 218 219 I2C_SET(dev,0,1); 220 for (i=7; i>=0; i--) 221 { 222 I2C_SET(dev,1,1); 223 if (I2C_GET(dev)) 224 data |= (1<<i); 225 I2C_SET(dev,0,1); 226 } 227 last ? iicbb_one(dev) : iicbb_zero(dev); 228 I2C_DEBUG(printf("r%02x%c ",(int)data,last?'-':'+')); 229 return data; 230 } 231 232 static int iicbb_callback(device_t dev, int index, caddr_t data) 233 { 234 return (IICBB_CALLBACK(device_get_parent(dev), index, data)); 235 } 236 237 static int iicbb_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr) 238 { 239 return (IICBB_RESET(device_get_parent(dev), speed, addr, oldaddr)); 240 } 241 242 static int iicbb_start(device_t dev, u_char slave, int timeout) 243 { 244 int error; 245 246 I2C_DEBUG(printf("<")); 247 248 I2C_SET(dev,0,1); 249 I2C_SET(dev,1,1); 250 I2C_SET(dev,1,0); 251 I2C_SET(dev,0,0); 252 253 /* send address */ 254 iicbb_sendbyte(dev, slave); 255 256 /* check for ack */ 257 if (iicbb_ack(dev, timeout)) { 258 error = IIC_ENOACK; 259 goto error; 260 } 261 262 return(0); 263 264 error: 265 iicbb_stop(dev); 266 return (error); 267 } 268 269 static int iicbb_stop(device_t dev) 270 { 271 I2C_SET(dev,0,0); 272 I2C_SET(dev,1,0); 273 I2C_SET(dev,1,1); 274 I2C_DEBUG(printf(">")); 275 return (0); 276 } 277 278 static int iicbb_write(device_t dev, char * buf, int len, int *sent, 279 int timeout) 280 { 281 int bytes, error = 0; 282 283 bytes = 0; 284 while (len) { 285 /* send byte */ 286 iicbb_sendbyte(dev,(u_char)*buf++); 287 288 /* check for ack */ 289 if (iicbb_ack(dev, timeout)) { 290 error = IIC_ENOACK; 291 goto error; 292 } 293 bytes ++; 294 len --; 295 } 296 297 error: 298 *sent = bytes; 299 return (error); 300 } 301 302 static int iicbb_read(device_t dev, char * buf, int len, int *read, 303 int last, int delay) 304 { 305 int bytes; 306 307 bytes = 0; 308 while (len) { 309 /* XXX should insert delay here */ 310 *buf++ = (char)iicbb_readbyte(dev, (len == 1) ? last : 0); 311 312 bytes ++; 313 len --; 314 } 315 316 *read = bytes; 317 return (0); 318 } 319 320 DRIVER_MODULE(iicbb, bti2c, iicbb_driver, iicbb_devclass, 0, 0); 321 DRIVER_MODULE(iicbb, lpbb, iicbb_driver, iicbb_devclass, 0, 0); 322