icee.c revision 167857
1167857Simp/*- 2167857Simp * Copyright (c) 2006 Warner Losh. All rights reserved. 3167857Simp * 4167857Simp * Redistribution and use in source and binary forms, with or without 5167857Simp * modification, are permitted provided that the following conditions 6167857Simp * are met: 7167857Simp * 1. Redistributions of source code must retain the above copyright 8167857Simp * notice, this list of conditions and the following disclaimer. 9167857Simp * 2. Redistributions in binary form must reproduce the above copyright 10167857Simp * notice, this list of conditions and the following disclaimer in the 11167857Simp * documentation and/or other materials provided with the distribution. 12167857Simp * 13167857Simp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 14167857Simp * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 15167857Simp * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 16167857Simp * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 17167857Simp * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 18167857Simp * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 19167857Simp * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 20167857Simp * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21167857Simp * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22167857Simp * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23167857Simp */ 24167857Simp 25167857Simp#include <sys/cdefs.h> 26167857Simp__FBSDID("$FreeBSD: head/sys/dev/iicbus/icee.c 167857 2007-03-23 23:10:35Z imp $"); 27167857Simp/* 28167857Simp * Generic IIC eeprom support, modeled after the AT24C family of products. 29167857Simp */ 30167857Simp#include <sys/param.h> 31167857Simp#include <sys/systm.h> 32167857Simp#include <sys/bus.h> 33167857Simp#include <sys/conf.h> 34167857Simp#include <sys/kernel.h> 35167857Simp#include <sys/module.h> 36167857Simp#include <sys/mutex.h> 37167857Simp#include <sys/resource.h> 38167857Simp#include <sys/uio.h> 39167857Simp#include <machine/bus.h> 40167857Simp#include <dev/iicbus/iiconf.h> 41167857Simp#include <dev/iicbus/iicbus.h> 42167857Simp 43167857Simp#include "iicbus_if.h" 44167857Simp 45167857Simp#define IIC_M_WR 0 /* write operation */ 46167857Simp#define MAX_RD_SZ 256 /* Largest read size we support */ 47167857Simp#define MAX_WR_SZ 256 /* Largest write size we support */ 48167857Simp 49167857Simpstruct icee_softc { 50167857Simp device_t sc_dev; /* Myself */ 51167857Simp struct mtx sc_mtx; /* basically a perimeter lock */ 52167857Simp struct cdev *cdev; /* user interface */ 53167857Simp int addr; 54167857Simp int flags; 55167857Simp#define OPENED 1 56167857Simp int size; /* How big am I? */ 57167857Simp int type; /* What type 8 or 16 bit? */ 58167857Simp int rd_sz; /* What's the read page size */ 59167857Simp int wr_sz; /* What's the write page size */ 60167857Simp}; 61167857Simp 62167857Simp#define ICEE_LOCK(_sc) mtx_lock_spin(&(_sc)->sc_mtx) 63167857Simp#define ICEE_UNLOCK(_sc) mtx_unlock_spin(&(_sc)->sc_mtx) 64167857Simp#define ICEE_LOCK_INIT(_sc) \ 65167857Simp mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->sc_dev), "icee", MTX_SPIN) 66167857Simp#define ICEE_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx); 67167857Simp#define ICEE_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED); 68167857Simp#define ICEE_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED); 69167857Simp#define CDEV2SOFTC(dev) ((dev)->si_drv1) 70167857Simp 71167857Simp/* cdev routines */ 72167857Simpstatic d_open_t icee_open; 73167857Simpstatic d_close_t icee_close; 74167857Simpstatic d_read_t icee_read; 75167857Simpstatic d_write_t icee_write; 76167857Simp 77167857Simpstatic struct cdevsw icee_cdevsw = 78167857Simp{ 79167857Simp .d_version = D_VERSION, 80167857Simp .d_open = icee_open, 81167857Simp .d_close = icee_close, 82167857Simp .d_read = icee_read, 83167857Simp .d_write = icee_write 84167857Simp}; 85167857Simp 86167857Simpstatic int 87167857Simpicee_probe(device_t dev) 88167857Simp{ 89167857Simp /* XXX really probe? -- not until we know the size... */ 90167857Simp device_set_desc(dev, "I2C EEPROM"); 91167857Simp return (0); 92167857Simp} 93167857Simp 94167857Simpstatic int 95167857Simpicee_attach(device_t dev) 96167857Simp{ 97167857Simp struct icee_softc *sc = device_get_softc(dev); 98167857Simp const char *dname; 99167857Simp int dunit, err; 100167857Simp 101167857Simp sc->sc_dev = dev; 102167857Simp iicbus_get_addr(dev, &sc->addr); 103167857Simp err = 0; 104167857Simp dname = device_get_name(dev); 105167857Simp dunit = device_get_unit(dev); 106167857Simp resource_int_value(dname, dunit, "size", &sc->size); 107167857Simp resource_int_value(dname, dunit, "type", &sc->type); 108167857Simp resource_int_value(dname, dunit, "rd_sz", &sc->rd_sz); 109167857Simp if (sc->rd_sz > MAX_RD_SZ) 110167857Simp sc->rd_sz = MAX_RD_SZ; 111167857Simp resource_int_value(dname, dunit, "wr_sz", &sc->wr_sz); 112167857Simp if (bootverbose) 113167857Simp device_printf(dev, "size: %d bytes bus_width: %d-bits\n", 114167857Simp sc->size, sc->type); 115167857Simp sc->cdev = make_dev(&icee_cdevsw, device_get_unit(dev), UID_ROOT, 116167857Simp GID_WHEEL, 0600, "icee%d", device_get_unit(dev)); 117167857Simp if (sc->cdev == NULL) { 118167857Simp err = ENOMEM; 119167857Simp goto out; 120167857Simp } 121167857Simp sc->cdev->si_drv1 = sc; 122167857Simp ICEE_LOCK_INIT(sc); 123167857Simpout:; 124167857Simp return (err); 125167857Simp} 126167857Simp 127167857Simpstatic int 128167857Simpicee_open(struct cdev *dev, int oflags, int devtype, struct thread *td) 129167857Simp{ 130167857Simp struct icee_softc *sc; 131167857Simp 132167857Simp sc = CDEV2SOFTC(dev); 133167857Simp ICEE_LOCK(sc); 134167857Simp if (!(sc->flags & OPENED)) { 135167857Simp sc->flags |= OPENED; 136167857Simp } 137167857Simp ICEE_UNLOCK(sc); 138167857Simp return (0); 139167857Simp} 140167857Simp 141167857Simpstatic int 142167857Simpicee_close(struct cdev *dev, int fflag, int devtype, struct thread *td) 143167857Simp{ 144167857Simp struct icee_softc *sc; 145167857Simp 146167857Simp sc = CDEV2SOFTC(dev); 147167857Simp ICEE_LOCK(sc); 148167857Simp sc->flags &= ~OPENED; 149167857Simp ICEE_UNLOCK(sc); 150167857Simp return (0); 151167857Simp} 152167857Simp 153167857Simpstatic int 154167857Simpicee_read(struct cdev *dev, struct uio *uio, int ioflag) 155167857Simp{ 156167857Simp struct icee_softc *sc; 157167857Simp uint8_t addr[2]; 158167857Simp uint8_t data[MAX_RD_SZ]; 159167857Simp int error, i, len, slave; 160167857Simp struct iic_msg msgs[2] = { 161167857Simp { 0, IIC_M_WR, 1, addr }, 162167857Simp { 0, IIC_M_RD, 0, data }, 163167857Simp }; 164167857Simp 165167857Simp sc = CDEV2SOFTC(dev); 166167857Simp if (uio->uio_offset == sc->size) 167167857Simp return (0); 168167857Simp if (uio->uio_offset > sc->size) 169167857Simp return (EIO); 170167857Simp if (sc->type != 8 && sc->type != 16) 171167857Simp return (EINVAL); 172167857Simp ICEE_LOCK(sc); 173167857Simp slave = error = 0; 174167857Simp while (uio->uio_resid > 0) { 175167857Simp if (uio->uio_offset >= sc->size) 176167857Simp break; 177167857Simp len = MIN(sc->rd_sz - (uio->uio_offset & (sc->rd_sz - 1)), 178167857Simp uio->uio_resid); 179167857Simp switch (sc->type) { 180167857Simp case 8: 181167857Simp slave = (uio->uio_offset >> 7) | sc->addr; 182167857Simp msgs[0].len = 1; 183167857Simp msgs[1].len = len; 184167857Simp addr[0] = uio->uio_offset & 0xff; 185167857Simp break; 186167857Simp case 16: 187167857Simp slave = sc->addr | (uio->uio_offset >> 15); 188167857Simp msgs[0].len = 2; 189167857Simp msgs[1].len = len; 190167857Simp addr[0] = (uio->uio_offset >> 8) & 0xff; 191167857Simp addr[1] = uio->uio_offset & 0xff; 192167857Simp break; 193167857Simp } 194167857Simp for (i = 0; i < 2; i++) 195167857Simp msgs[i].slave = slave; 196167857Simp error = iicbus_transfer(sc->sc_dev, msgs, 2); 197167857Simp if (error) 198167857Simp break; 199167857Simp error = uiomove(data, len, uio); 200167857Simp if (error) 201167857Simp break; 202167857Simp } 203167857Simp ICEE_UNLOCK(sc); 204167857Simp return (error); 205167857Simp} 206167857Simp 207167857Simp/* 208167857Simp * Write to the part. We use three transfers here since we're actually 209167857Simp * doing a write followed by a read to make sure that the write finished. 210167857Simp * It is easier to encode the dummy read here than to break things up 211167857Simp * into smaller chunks... 212167857Simp */ 213167857Simpstatic int 214167857Simpicee_write(struct cdev *dev, struct uio *uio, int ioflag) 215167857Simp{ 216167857Simp struct icee_softc *sc; 217167857Simp int error, len, slave, waitlimit; 218167857Simp uint8_t data[MAX_WR_SZ + 2]; 219167857Simp struct iic_msg wr[1] = { 220167857Simp { 0, IIC_M_WR, 0, data }, 221167857Simp }; 222167857Simp struct iic_msg rd[1] = { 223167857Simp { 0, IIC_M_RD, 1, data }, 224167857Simp }; 225167857Simp 226167857Simp sc = CDEV2SOFTC(dev); 227167857Simp if (uio->uio_offset >= sc->size) 228167857Simp return (EIO); 229167857Simp if (sc->type != 8 && sc->type != 16) 230167857Simp return (EINVAL); 231167857Simp ICEE_LOCK(sc); 232167857Simp slave = error = 0; 233167857Simp while (uio->uio_resid > 0) { 234167857Simp if (uio->uio_offset >= sc->size) 235167857Simp break; 236167857Simp len = MIN(sc->wr_sz - (uio->uio_offset & (sc->wr_sz - 1)), 237167857Simp uio->uio_resid); 238167857Simp switch (sc->type) { 239167857Simp case 8: 240167857Simp slave = (uio->uio_offset >> 7) | sc->addr; 241167857Simp wr[0].len = 1 + len; 242167857Simp data[0] = uio->uio_offset & 0xff; 243167857Simp break; 244167857Simp case 16: 245167857Simp slave = sc->addr | (uio->uio_offset >> 15); 246167857Simp wr[0].len = 2 + len; 247167857Simp data[0] = (uio->uio_offset >> 8) & 0xff; 248167857Simp data[1] = uio->uio_offset & 0xff; 249167857Simp break; 250167857Simp } 251167857Simp wr[0].slave = slave; 252167857Simp error = uiomove(data + sc->type / 8, len, uio); 253167857Simp if (error) 254167857Simp break; 255167857Simp error = iicbus_transfer(sc->sc_dev, wr, 1); 256167857Simp if (error) 257167857Simp break; 258167857Simp // Now wait for the write to be done by trying to read 259167857Simp // the part. 260167857Simp waitlimit = 10000; 261167857Simp rd[0].slave = slave; 262167857Simp do 263167857Simp { 264167857Simp error = iicbus_transfer(sc->sc_dev, rd, 1); 265167857Simp } while (waitlimit-- > 0 && error != 0); 266167857Simp if (error) { 267167857Simp printf("waiting for write failed %d\n", error); 268167857Simp break; 269167857Simp } 270167857Simp } 271167857Simp ICEE_UNLOCK(sc); 272167857Simp return error; 273167857Simp} 274167857Simp 275167857Simpstatic device_method_t icee_methods[] = { 276167857Simp DEVMETHOD(device_probe, icee_probe), 277167857Simp DEVMETHOD(device_attach, icee_attach), 278167857Simp 279167857Simp {0, 0}, 280167857Simp}; 281167857Simp 282167857Simpstatic driver_t icee_driver = { 283167857Simp "icee", 284167857Simp icee_methods, 285167857Simp sizeof(struct icee_softc), 286167857Simp}; 287167857Simpstatic devclass_t icee_devclass; 288167857Simp 289167857SimpDRIVER_MODULE(icee, iicbus, icee_driver, icee_devclass, 0, 0); 290167857SimpMODULE_VERSION(icee, 1); 291167857SimpMODULE_DEPEND(icee, iicbus, 1, 1, 1); 292