icee.c revision 289105
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 289105 2015-10-10 02:29:02Z ian $"); 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{ 87289083Sian 88167857Simp device_set_desc(dev, "I2C EEPROM"); 89186833Snwhitehorn return (BUS_PROBE_NOWILDCARD); 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); 121289083Sianout: 122167857Simp return (err); 123167857Simp} 124167857Simp 125167857Simpstatic int 126167857Simpicee_open(struct cdev *dev, int oflags, int devtype, struct thread *td) 127167857Simp{ 128167857Simp 129289083Sian 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); 183289105Sian if (error) { 184289105Sian error = iic2errno(error); 185167857Simp break; 186289105Sian } 187167857Simp error = uiomove(data, len, uio); 188167857Simp if (error) 189167857Simp break; 190167857Simp } 191167857Simp ICEE_UNLOCK(sc); 192167857Simp return (error); 193167857Simp} 194167857Simp 195167857Simp/* 196167857Simp * Write to the part. We use three transfers here since we're actually 197167857Simp * doing a write followed by a read to make sure that the write finished. 198167857Simp * It is easier to encode the dummy read here than to break things up 199167857Simp * into smaller chunks... 200167857Simp */ 201167857Simpstatic int 202167857Simpicee_write(struct cdev *dev, struct uio *uio, int ioflag) 203167857Simp{ 204167857Simp struct icee_softc *sc; 205167857Simp int error, len, slave, waitlimit; 206167857Simp uint8_t data[MAX_WR_SZ + 2]; 207167857Simp struct iic_msg wr[1] = { 208167857Simp { 0, IIC_M_WR, 0, data }, 209167857Simp }; 210167857Simp struct iic_msg rd[1] = { 211167857Simp { 0, IIC_M_RD, 1, data }, 212167857Simp }; 213167857Simp 214167857Simp sc = CDEV2SOFTC(dev); 215167857Simp if (uio->uio_offset >= sc->size) 216167857Simp return (EIO); 217167857Simp if (sc->type != 8 && sc->type != 16) 218167857Simp return (EINVAL); 219167857Simp ICEE_LOCK(sc); 220167857Simp slave = error = 0; 221167857Simp while (uio->uio_resid > 0) { 222167857Simp if (uio->uio_offset >= sc->size) 223167857Simp break; 224167857Simp len = MIN(sc->wr_sz - (uio->uio_offset & (sc->wr_sz - 1)), 225167857Simp uio->uio_resid); 226167857Simp switch (sc->type) { 227167857Simp case 8: 228167857Simp slave = (uio->uio_offset >> 7) | sc->addr; 229167857Simp wr[0].len = 1 + len; 230167857Simp data[0] = uio->uio_offset & 0xff; 231167857Simp break; 232167857Simp case 16: 233167857Simp slave = sc->addr | (uio->uio_offset >> 15); 234167857Simp wr[0].len = 2 + len; 235167857Simp data[0] = (uio->uio_offset >> 8) & 0xff; 236167857Simp data[1] = uio->uio_offset & 0xff; 237167857Simp break; 238167857Simp } 239167857Simp wr[0].slave = slave; 240167857Simp error = uiomove(data + sc->type / 8, len, uio); 241167857Simp if (error) 242167857Simp break; 243167857Simp error = iicbus_transfer(sc->sc_dev, wr, 1); 244289105Sian if (error) { 245289105Sian error = iic2errno(error); 246167857Simp break; 247289105Sian } 248289083Sian /* Read after write to wait for write-done. */ 249167857Simp waitlimit = 10000; 250167857Simp rd[0].slave = slave; 251289083Sian do { 252289083Sian error = iicbus_transfer(sc->sc_dev, rd, 1); 253167857Simp } while (waitlimit-- > 0 && error != 0); 254289105Sian if (error) { 255289105Sian error = iic2errno(error); 256289083Sian break; 257289105Sian } 258167857Simp } 259167857Simp ICEE_UNLOCK(sc); 260167857Simp return error; 261167857Simp} 262167857Simp 263167857Simpstatic device_method_t icee_methods[] = { 264167857Simp DEVMETHOD(device_probe, icee_probe), 265167857Simp DEVMETHOD(device_attach, icee_attach), 266167857Simp 267246128Ssbz DEVMETHOD_END 268167857Simp}; 269167857Simp 270167857Simpstatic driver_t icee_driver = { 271167857Simp "icee", 272167857Simp icee_methods, 273167857Simp sizeof(struct icee_softc), 274167857Simp}; 275167857Simpstatic devclass_t icee_devclass; 276167857Simp 277167857SimpDRIVER_MODULE(icee, iicbus, icee_driver, icee_devclass, 0, 0); 278167857SimpMODULE_VERSION(icee, 1); 279167857SimpMODULE_DEPEND(icee, iicbus, 1, 1, 1); 280