iiconf.c revision 42442
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 */ 43void 44iicbus_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 */ 60device_t 61iicbus_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 71static int 72iicbus_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 */ 100int 101iicbus_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 */ 136int 137iicbus_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 */ 168int 169iicbus_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 */ 181int 182iicbus_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 */ 203int 204iicbus_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 */ 226int 227iicbus_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 */ 244int 245iicbus_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 */ 261int 262iicbus_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 */ 282int 283iicbus_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 */ 303u_char 304iicbus_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