icee.c revision 181325
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 181325 2008-08-05 08:38:33Z stas $"); 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/resource.h> 37181305Sjhb#include <sys/sx.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 */ 51181305Sjhb struct sx sc_lock; /* basically a perimeter lock */ 52167857Simp struct cdev *cdev; /* user interface */ 53167857Simp int addr; 54167857Simp int size; /* How big am I? */ 55167857Simp int type; /* What type 8 or 16 bit? */ 56167857Simp int rd_sz; /* What's the read page size */ 57167857Simp int wr_sz; /* What's the write page size */ 58167857Simp}; 59167857Simp 60181305Sjhb#define ICEE_LOCK(_sc) sx_xlock(&(_sc)->sc_lock) 61181305Sjhb#define ICEE_UNLOCK(_sc) sx_xunlock(&(_sc)->sc_lock) 62181305Sjhb#define ICEE_LOCK_INIT(_sc) sx_init(&_sc->sc_lock, "icee") 63181305Sjhb#define ICEE_LOCK_DESTROY(_sc) sx_destroy(&_sc->sc_lock); 64181305Sjhb#define ICEE_ASSERT_LOCKED(_sc) sx_assert(&_sc->sc_lock, SA_XLOCKED); 65181305Sjhb#define ICEE_ASSERT_UNLOCKED(_sc) sx_assert(&_sc->sc_lock, SA_UNLOCKED); 66167857Simp#define CDEV2SOFTC(dev) ((dev)->si_drv1) 67167857Simp 68167857Simp/* cdev routines */ 69167857Simpstatic d_open_t icee_open; 70167857Simpstatic d_close_t icee_close; 71167857Simpstatic d_read_t icee_read; 72167857Simpstatic d_write_t icee_write; 73167857Simp 74167857Simpstatic struct cdevsw icee_cdevsw = 75167857Simp{ 76167857Simp .d_version = D_VERSION, 77181305Sjhb .d_flags = D_TRACKCLOSE, 78167857Simp .d_open = icee_open, 79167857Simp .d_close = icee_close, 80167857Simp .d_read = icee_read, 81167857Simp .d_write = icee_write 82167857Simp}; 83167857Simp 84167857Simpstatic int 85167857Simpicee_probe(device_t dev) 86167857Simp{ 87167857Simp /* XXX really probe? -- not until we know the size... */ 88167857Simp device_set_desc(dev, "I2C EEPROM"); 89167857Simp return (0); 90167857Simp} 91167857Simp 92167857Simpstatic int 93167857Simpicee_attach(device_t dev) 94167857Simp{ 95167857Simp struct icee_softc *sc = device_get_softc(dev); 96167857Simp const char *dname; 97167857Simp int dunit, err; 98167857Simp 99167857Simp sc->sc_dev = dev; 100181325Sstas sc->addr = iicbus_get_addr(dev); 101167857Simp err = 0; 102167857Simp dname = device_get_name(dev); 103167857Simp dunit = device_get_unit(dev); 104167857Simp resource_int_value(dname, dunit, "size", &sc->size); 105167857Simp resource_int_value(dname, dunit, "type", &sc->type); 106167857Simp resource_int_value(dname, dunit, "rd_sz", &sc->rd_sz); 107167857Simp if (sc->rd_sz > MAX_RD_SZ) 108167857Simp sc->rd_sz = MAX_RD_SZ; 109167857Simp resource_int_value(dname, dunit, "wr_sz", &sc->wr_sz); 110167857Simp if (bootverbose) 111167857Simp device_printf(dev, "size: %d bytes bus_width: %d-bits\n", 112167857Simp sc->size, sc->type); 113167857Simp sc->cdev = make_dev(&icee_cdevsw, device_get_unit(dev), UID_ROOT, 114167857Simp GID_WHEEL, 0600, "icee%d", device_get_unit(dev)); 115167857Simp if (sc->cdev == NULL) { 116167857Simp err = ENOMEM; 117167857Simp goto out; 118167857Simp } 119167857Simp sc->cdev->si_drv1 = sc; 120167857Simp ICEE_LOCK_INIT(sc); 121167857Simpout:; 122167857Simp return (err); 123167857Simp} 124167857Simp 125167857Simpstatic int 126167857Simpicee_open(struct cdev *dev, int oflags, int devtype, struct thread *td) 127167857Simp{ 128167857Simp 129167857Simp return (0); 130167857Simp} 131167857Simp 132167857Simpstatic int 133167857Simpicee_close(struct cdev *dev, int fflag, int devtype, struct thread *td) 134167857Simp{ 135167857Simp 136167857Simp return (0); 137167857Simp} 138167857Simp 139167857Simpstatic int 140167857Simpicee_read(struct cdev *dev, struct uio *uio, int ioflag) 141167857Simp{ 142167857Simp struct icee_softc *sc; 143167857Simp uint8_t addr[2]; 144167857Simp uint8_t data[MAX_RD_SZ]; 145167857Simp int error, i, len, slave; 146167857Simp struct iic_msg msgs[2] = { 147167857Simp { 0, IIC_M_WR, 1, addr }, 148167857Simp { 0, IIC_M_RD, 0, data }, 149167857Simp }; 150167857Simp 151167857Simp sc = CDEV2SOFTC(dev); 152167857Simp if (uio->uio_offset == sc->size) 153167857Simp return (0); 154167857Simp if (uio->uio_offset > sc->size) 155167857Simp return (EIO); 156167857Simp if (sc->type != 8 && sc->type != 16) 157167857Simp return (EINVAL); 158167857Simp ICEE_LOCK(sc); 159167857Simp slave = error = 0; 160167857Simp while (uio->uio_resid > 0) { 161167857Simp if (uio->uio_offset >= sc->size) 162167857Simp break; 163167857Simp len = MIN(sc->rd_sz - (uio->uio_offset & (sc->rd_sz - 1)), 164167857Simp uio->uio_resid); 165167857Simp switch (sc->type) { 166167857Simp case 8: 167167857Simp slave = (uio->uio_offset >> 7) | sc->addr; 168167857Simp msgs[0].len = 1; 169167857Simp msgs[1].len = len; 170167857Simp addr[0] = uio->uio_offset & 0xff; 171167857Simp break; 172167857Simp case 16: 173167857Simp slave = sc->addr | (uio->uio_offset >> 15); 174167857Simp msgs[0].len = 2; 175167857Simp msgs[1].len = len; 176167857Simp addr[0] = (uio->uio_offset >> 8) & 0xff; 177167857Simp addr[1] = uio->uio_offset & 0xff; 178167857Simp break; 179167857Simp } 180167857Simp for (i = 0; i < 2; i++) 181167857Simp msgs[i].slave = slave; 182167857Simp error = iicbus_transfer(sc->sc_dev, msgs, 2); 183167857Simp if (error) 184167857Simp break; 185167857Simp error = uiomove(data, len, uio); 186167857Simp if (error) 187167857Simp break; 188167857Simp } 189167857Simp ICEE_UNLOCK(sc); 190167857Simp return (error); 191167857Simp} 192167857Simp 193167857Simp/* 194167857Simp * Write to the part. We use three transfers here since we're actually 195167857Simp * doing a write followed by a read to make sure that the write finished. 196167857Simp * It is easier to encode the dummy read here than to break things up 197167857Simp * into smaller chunks... 198167857Simp */ 199167857Simpstatic int 200167857Simpicee_write(struct cdev *dev, struct uio *uio, int ioflag) 201167857Simp{ 202167857Simp struct icee_softc *sc; 203167857Simp int error, len, slave, waitlimit; 204167857Simp uint8_t data[MAX_WR_SZ + 2]; 205167857Simp struct iic_msg wr[1] = { 206167857Simp { 0, IIC_M_WR, 0, data }, 207167857Simp }; 208167857Simp struct iic_msg rd[1] = { 209167857Simp { 0, IIC_M_RD, 1, data }, 210167857Simp }; 211167857Simp 212167857Simp sc = CDEV2SOFTC(dev); 213167857Simp if (uio->uio_offset >= sc->size) 214167857Simp return (EIO); 215167857Simp if (sc->type != 8 && sc->type != 16) 216167857Simp return (EINVAL); 217167857Simp ICEE_LOCK(sc); 218167857Simp slave = error = 0; 219167857Simp while (uio->uio_resid > 0) { 220167857Simp if (uio->uio_offset >= sc->size) 221167857Simp break; 222167857Simp len = MIN(sc->wr_sz - (uio->uio_offset & (sc->wr_sz - 1)), 223167857Simp uio->uio_resid); 224167857Simp switch (sc->type) { 225167857Simp case 8: 226167857Simp slave = (uio->uio_offset >> 7) | sc->addr; 227167857Simp wr[0].len = 1 + len; 228167857Simp data[0] = uio->uio_offset & 0xff; 229167857Simp break; 230167857Simp case 16: 231167857Simp slave = sc->addr | (uio->uio_offset >> 15); 232167857Simp wr[0].len = 2 + len; 233167857Simp data[0] = (uio->uio_offset >> 8) & 0xff; 234167857Simp data[1] = uio->uio_offset & 0xff; 235167857Simp break; 236167857Simp } 237167857Simp wr[0].slave = slave; 238167857Simp error = uiomove(data + sc->type / 8, len, uio); 239167857Simp if (error) 240167857Simp break; 241167857Simp error = iicbus_transfer(sc->sc_dev, wr, 1); 242167857Simp if (error) 243167857Simp break; 244167857Simp // Now wait for the write to be done by trying to read 245167857Simp // the part. 246167857Simp waitlimit = 10000; 247167857Simp rd[0].slave = slave; 248167857Simp do 249167857Simp { 250167857Simp error = iicbus_transfer(sc->sc_dev, rd, 1); 251167857Simp } while (waitlimit-- > 0 && error != 0); 252167857Simp if (error) { 253167857Simp printf("waiting for write failed %d\n", error); 254167857Simp break; 255167857Simp } 256167857Simp } 257167857Simp ICEE_UNLOCK(sc); 258167857Simp return error; 259167857Simp} 260167857Simp 261167857Simpstatic device_method_t icee_methods[] = { 262167857Simp DEVMETHOD(device_probe, icee_probe), 263167857Simp DEVMETHOD(device_attach, icee_attach), 264167857Simp 265167857Simp {0, 0}, 266167857Simp}; 267167857Simp 268167857Simpstatic driver_t icee_driver = { 269167857Simp "icee", 270167857Simp icee_methods, 271167857Simp sizeof(struct icee_softc), 272167857Simp}; 273167857Simpstatic devclass_t icee_devclass; 274167857Simp 275167857SimpDRIVER_MODULE(icee, iicbus, icee_driver, icee_devclass, 0, 0); 276167857SimpMODULE_VERSION(icee, 1); 277167857SimpMODULE_DEPEND(icee, iicbus, 1, 1, 1); 278