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 * $Id: iiconf.c,v 1.3 1998/11/22 22:01:42 nsouch Exp $ 27 * 28 */ 29 #include <sys/param.h> 30 #include <sys/systm.h> 31 #include <sys/kernel.h> 32 #include <sys/malloc.h> 33 #include <sys/module.h> 34 #include <sys/bus.h> 35 36 #include <dev/iicbus/iiconf.h> 37 #include <dev/iicbus/iicbus.h> 38 #include "iicbus_if.h" 39 40 /* 41 * iicbus_intr() 42 */ 43 void 44 iicbus_intr(device_t bus, int event, char *buf) 45 { 46 struct iicbus_softc *sc = (struct iicbus_softc *)device_get_softc(bus); 47 48 /* call owner's intr routine */ 49 if (sc->owner) 50 IICBUS_INTR(sc->owner, event, buf); 51 52 return; 53 } 54 55 /* 56 * iicbus_alloc_bus() 57 * 58 * Allocate a new bus connected to the given parent device 59 */ 60 device_t 61 iicbus_alloc_bus(device_t parent) 62 { 63 device_t child; 64 65 /* add the bus to the parent */ 66 child = device_add_child(parent, "iicbus", -1, NULL); 67 68 return (child); 69 } 70 71 static int 72 iicbus_poll(struct iicbus_softc *sc, int how) 73 { 74 int error; 75 76 switch (how) { 77 case (IIC_WAIT | IIC_INTR): 78 error = tsleep(sc, IICPRI|PCATCH, "iicreq", 0); 79 break; 80 81 case (IIC_WAIT | IIC_NOINTR): 82 error = tsleep(sc, IICPRI, "iicreq", 0); 83 break; 84 85 default: 86 return (EWOULDBLOCK); 87 break; 88 } 89 90 return (error); 91 } 92 93 /* 94 * iicbus_request_bus() 95 * 96 * Allocate the device to perform transfers. 97 * 98 * how : IIC_WAIT or IIC_DONTWAIT 99 */ 100 int 101 iicbus_request_bus(device_t bus, device_t dev, int how) 102 { 103 struct iicbus_softc *sc = (struct iicbus_softc *)device_get_softc(bus); 104 int s, error = 0; 105 106 /* first, ask the underlying layers if the request is ok */ 107 do { 108 error = IICBUS_CALLBACK(device_get_parent(bus), 109 IIC_REQUEST_BUS, (caddr_t)&how); 110 if (error) 111 error = iicbus_poll(sc, how); 112 } while (error); 113 114 while (!error) { 115 s = splhigh(); 116 if (sc->owner) { 117 splx(s); 118 119 error = iicbus_poll(sc, how); 120 } else { 121 sc->owner = dev; 122 123 splx(s); 124 return (0); 125 } 126 } 127 128 return (error); 129 } 130 131 /* 132 * iicbus_release_bus() 133 * 134 * Release the device allocated with iicbus_request_dev() 135 */ 136 int 137 iicbus_release_bus(device_t bus, device_t dev) 138 { 139 struct iicbus_softc *sc = (struct iicbus_softc *)device_get_softc(bus); 140 int s, error; 141 142 /* first, ask the underlying layers if the release is ok */ 143 error = IICBUS_CALLBACK(device_get_parent(bus), IIC_RELEASE_BUS, NULL); 144 145 if (error) 146 return (error); 147 148 s = splhigh(); 149 if (sc->owner != dev) { 150 splx(s); 151 return (EACCES); 152 } 153 154 sc->owner = 0; 155 splx(s); 156 157 /* wakeup waiting processes */ 158 wakeup(sc); 159 160 return (0); 161 } 162 163 /* 164 * iicbus_started() 165 * 166 * Test if the iicbus is started by the controller 167 */ 168 int 169 iicbus_started(device_t bus) 170 { 171 struct iicbus_softc *sc = (struct iicbus_softc *)device_get_softc(bus); 172 173 return (sc->started); 174 } 175 176 /* 177 * iicbus_start() 178 * 179 * Send start condition to the slave addressed by 'slave' 180 */ 181 int 182 iicbus_start(device_t bus, u_char slave, int timeout) 183 { 184 struct iicbus_softc *sc = (struct iicbus_softc *)device_get_softc(bus); 185 int error = 0; 186 187 if (sc->started) 188 return (EINVAL); /* bus already started */ 189 190 if (!(error = IICBUS_START(device_get_parent(bus), slave, timeout))) 191 sc->started = slave; 192 else 193 sc->started = 0; 194 195 return (error); 196 } 197 198 /* 199 * iicbus_stop() 200 * 201 * Send stop condition to the bus 202 */ 203 int 204 iicbus_stop(device_t bus) 205 { 206 struct iicbus_softc *sc = (struct iicbus_softc *)device_get_softc(bus); 207 int error = 0; 208 209 if (!sc->started) 210 return (EINVAL); /* bus not started */ 211 212 error = IICBUS_STOP(device_get_parent(bus)); 213 214 /* refuse any further access */ 215 sc->started = 0; 216 217 return (error); 218 } 219 220 /* 221 * iicbus_write() 222 * 223 * Write a block of data to the slave previously started by 224 * iicbus_start() call 225 */ 226 int 227 iicbus_write(device_t bus, char *buf, int len, int *sent, int timeout) 228 { 229 struct iicbus_softc *sc = (struct iicbus_softc *)device_get_softc(bus); 230 231 /* a slave must have been started with the appropriate address */ 232 if (!sc->started || (sc->started & LSB)) 233 return (EINVAL); 234 235 return (IICBUS_WRITE(device_get_parent(bus), buf, len, sent, timeout)); 236 } 237 238 /* 239 * iicbus_read() 240 * 241 * Read a block of data from the slave previously started by 242 * iicbus_read() call 243 */ 244 int 245 iicbus_read(device_t bus, char *buf, int len, int *read, int last, int delay) 246 { 247 struct iicbus_softc *sc = (struct iicbus_softc *)device_get_softc(bus); 248 249 /* a slave must have been started with the appropriate address */ 250 if (!sc->started || !(sc->started & LSB)) 251 return (EINVAL); 252 253 return (IICBUS_READ(device_get_parent(bus), buf, len, read, last, delay)); 254 } 255 256 /* 257 * iicbus_block_write() 258 * 259 * Write a block of data to slave ; start/stop protocol managed 260 */ 261 int 262 iicbus_block_write(device_t bus, u_char slave, char *buf, int len, int *sent) 263 { 264 u_char addr = slave & ~LSB; 265 int error; 266 267 if ((error = iicbus_start(bus, addr, 0))) 268 return (error); 269 270 error = iicbus_write(bus, buf, len, sent, 0); 271 272 iicbus_stop(bus); 273 274 return (error); 275 } 276 277 /* 278 * iicbus_block_read() 279 * 280 * Read a block of data from slave ; start/stop protocol managed 281 */ 282 int 283 iicbus_block_read(device_t bus, u_char slave, char *buf, int len, int *read) 284 { 285 u_char addr = slave | LSB; 286 int error; 287 288 if ((error = iicbus_start(bus, addr, 0))) 289 return (error); 290 291 error = iicbus_read(bus, buf, len, read, IIC_LAST_READ, 0); 292 293 iicbus_stop(bus); 294 295 return (error); 296 } 297 298 /* 299 * iicbus_get_addr() 300 * 301 * Get the I2C 7 bits address of the device 302 */ 303 u_char 304 iicbus_get_addr(device_t dev) 305 { 306 uintptr_t addr; 307 device_t parent = device_get_parent(dev); 308 309 BUS_READ_IVAR(parent, dev, IICBUS_IVAR_ADDR, &addr); 310 311 return ((u_char)addr); 312 } 313 314