icee.c revision 289118
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 289118 2015-10-10 19:51:00Z 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 */ 51289118Sian device_t sc_busdev; /* Parent bus */ 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 60167857Simp#define CDEV2SOFTC(dev) ((dev)->si_drv1) 61167857Simp 62167857Simp/* cdev routines */ 63167857Simpstatic d_open_t icee_open; 64167857Simpstatic d_close_t icee_close; 65167857Simpstatic d_read_t icee_read; 66167857Simpstatic d_write_t icee_write; 67167857Simp 68167857Simpstatic struct cdevsw icee_cdevsw = 69167857Simp{ 70167857Simp .d_version = D_VERSION, 71181305Sjhb .d_flags = D_TRACKCLOSE, 72167857Simp .d_open = icee_open, 73167857Simp .d_close = icee_close, 74167857Simp .d_read = icee_read, 75167857Simp .d_write = icee_write 76167857Simp}; 77167857Simp 78167857Simpstatic int 79167857Simpicee_probe(device_t dev) 80167857Simp{ 81289083Sian 82167857Simp device_set_desc(dev, "I2C EEPROM"); 83186833Snwhitehorn return (BUS_PROBE_NOWILDCARD); 84167857Simp} 85167857Simp 86167857Simpstatic int 87167857Simpicee_attach(device_t dev) 88167857Simp{ 89167857Simp struct icee_softc *sc = device_get_softc(dev); 90167857Simp const char *dname; 91167857Simp int dunit, err; 92167857Simp 93167857Simp sc->sc_dev = dev; 94289118Sian sc->sc_busdev = device_get_parent(sc->sc_dev); 95181325Sstas sc->addr = iicbus_get_addr(dev); 96167857Simp err = 0; 97167857Simp dname = device_get_name(dev); 98167857Simp dunit = device_get_unit(dev); 99167857Simp resource_int_value(dname, dunit, "size", &sc->size); 100167857Simp resource_int_value(dname, dunit, "type", &sc->type); 101167857Simp resource_int_value(dname, dunit, "rd_sz", &sc->rd_sz); 102167857Simp if (sc->rd_sz > MAX_RD_SZ) 103167857Simp sc->rd_sz = MAX_RD_SZ; 104167857Simp resource_int_value(dname, dunit, "wr_sz", &sc->wr_sz); 105167857Simp if (bootverbose) 106167857Simp device_printf(dev, "size: %d bytes bus_width: %d-bits\n", 107167857Simp sc->size, sc->type); 108167857Simp sc->cdev = make_dev(&icee_cdevsw, device_get_unit(dev), UID_ROOT, 109167857Simp GID_WHEEL, 0600, "icee%d", device_get_unit(dev)); 110167857Simp if (sc->cdev == NULL) { 111167857Simp err = ENOMEM; 112167857Simp goto out; 113167857Simp } 114167857Simp sc->cdev->si_drv1 = sc; 115289083Sianout: 116167857Simp return (err); 117167857Simp} 118167857Simp 119167857Simpstatic int 120167857Simpicee_open(struct cdev *dev, int oflags, int devtype, struct thread *td) 121167857Simp{ 122167857Simp 123289083Sian return (0); 124167857Simp} 125167857Simp 126167857Simpstatic int 127167857Simpicee_close(struct cdev *dev, int fflag, int devtype, struct thread *td) 128167857Simp{ 129167857Simp 130167857Simp return (0); 131167857Simp} 132167857Simp 133167857Simpstatic int 134167857Simpicee_read(struct cdev *dev, struct uio *uio, int ioflag) 135167857Simp{ 136167857Simp struct icee_softc *sc; 137167857Simp uint8_t addr[2]; 138167857Simp uint8_t data[MAX_RD_SZ]; 139167857Simp int error, i, len, slave; 140167857Simp struct iic_msg msgs[2] = { 141167857Simp { 0, IIC_M_WR, 1, addr }, 142167857Simp { 0, IIC_M_RD, 0, data }, 143167857Simp }; 144167857Simp 145167857Simp sc = CDEV2SOFTC(dev); 146167857Simp if (uio->uio_offset == sc->size) 147167857Simp return (0); 148167857Simp if (uio->uio_offset > sc->size) 149167857Simp return (EIO); 150167857Simp if (sc->type != 8 && sc->type != 16) 151167857Simp return (EINVAL); 152289118Sian error = iicbus_request_bus(sc->sc_busdev, sc->sc_dev, IIC_INTRWAIT); 153289118Sian if (error!= 0) 154289118Sian return (iic2errno(error)); 155167857Simp slave = error = 0; 156167857Simp while (uio->uio_resid > 0) { 157167857Simp if (uio->uio_offset >= sc->size) 158167857Simp break; 159167857Simp len = MIN(sc->rd_sz - (uio->uio_offset & (sc->rd_sz - 1)), 160167857Simp uio->uio_resid); 161167857Simp switch (sc->type) { 162167857Simp case 8: 163167857Simp slave = (uio->uio_offset >> 7) | sc->addr; 164167857Simp msgs[0].len = 1; 165167857Simp msgs[1].len = len; 166167857Simp addr[0] = uio->uio_offset & 0xff; 167167857Simp break; 168167857Simp case 16: 169167857Simp slave = sc->addr | (uio->uio_offset >> 15); 170167857Simp msgs[0].len = 2; 171167857Simp msgs[1].len = len; 172167857Simp addr[0] = (uio->uio_offset >> 8) & 0xff; 173167857Simp addr[1] = uio->uio_offset & 0xff; 174167857Simp break; 175167857Simp } 176167857Simp for (i = 0; i < 2; i++) 177167857Simp msgs[i].slave = slave; 178167857Simp error = iicbus_transfer(sc->sc_dev, msgs, 2); 179289105Sian if (error) { 180289105Sian error = iic2errno(error); 181167857Simp break; 182289105Sian } 183167857Simp error = uiomove(data, len, uio); 184167857Simp if (error) 185167857Simp break; 186167857Simp } 187289118Sian iicbus_release_bus(sc->sc_busdev, sc->sc_dev); 188167857Simp return (error); 189167857Simp} 190167857Simp 191167857Simp/* 192167857Simp * Write to the part. We use three transfers here since we're actually 193167857Simp * doing a write followed by a read to make sure that the write finished. 194167857Simp * It is easier to encode the dummy read here than to break things up 195167857Simp * into smaller chunks... 196167857Simp */ 197167857Simpstatic int 198167857Simpicee_write(struct cdev *dev, struct uio *uio, int ioflag) 199167857Simp{ 200167857Simp struct icee_softc *sc; 201167857Simp int error, len, slave, waitlimit; 202167857Simp uint8_t data[MAX_WR_SZ + 2]; 203167857Simp struct iic_msg wr[1] = { 204167857Simp { 0, IIC_M_WR, 0, data }, 205167857Simp }; 206167857Simp struct iic_msg rd[1] = { 207167857Simp { 0, IIC_M_RD, 1, data }, 208167857Simp }; 209167857Simp 210167857Simp sc = CDEV2SOFTC(dev); 211167857Simp if (uio->uio_offset >= sc->size) 212167857Simp return (EIO); 213167857Simp if (sc->type != 8 && sc->type != 16) 214167857Simp return (EINVAL); 215289118Sian 216289118Sian error = iicbus_request_bus(sc->sc_busdev, sc->sc_dev, IIC_INTRWAIT); 217289118Sian if (error!= 0) 218289118Sian return (iic2errno(error)); 219167857Simp slave = error = 0; 220167857Simp while (uio->uio_resid > 0) { 221167857Simp if (uio->uio_offset >= sc->size) 222167857Simp break; 223167857Simp len = MIN(sc->wr_sz - (uio->uio_offset & (sc->wr_sz - 1)), 224167857Simp uio->uio_resid); 225167857Simp switch (sc->type) { 226167857Simp case 8: 227167857Simp slave = (uio->uio_offset >> 7) | sc->addr; 228167857Simp wr[0].len = 1 + len; 229167857Simp data[0] = uio->uio_offset & 0xff; 230167857Simp break; 231167857Simp case 16: 232167857Simp slave = sc->addr | (uio->uio_offset >> 15); 233167857Simp wr[0].len = 2 + len; 234167857Simp data[0] = (uio->uio_offset >> 8) & 0xff; 235167857Simp data[1] = uio->uio_offset & 0xff; 236167857Simp break; 237167857Simp } 238167857Simp wr[0].slave = slave; 239167857Simp error = uiomove(data + sc->type / 8, len, uio); 240167857Simp if (error) 241167857Simp break; 242167857Simp error = iicbus_transfer(sc->sc_dev, wr, 1); 243289105Sian if (error) { 244289105Sian error = iic2errno(error); 245167857Simp break; 246289105Sian } 247289083Sian /* Read after write to wait for write-done. */ 248167857Simp waitlimit = 10000; 249167857Simp rd[0].slave = slave; 250289083Sian do { 251289083Sian error = iicbus_transfer(sc->sc_dev, rd, 1); 252167857Simp } while (waitlimit-- > 0 && error != 0); 253289105Sian if (error) { 254289105Sian error = iic2errno(error); 255289083Sian break; 256289105Sian } 257167857Simp } 258289118Sian iicbus_release_bus(sc->sc_busdev, sc->sc_dev); 259167857Simp return error; 260167857Simp} 261167857Simp 262167857Simpstatic device_method_t icee_methods[] = { 263167857Simp DEVMETHOD(device_probe, icee_probe), 264167857Simp DEVMETHOD(device_attach, icee_attach), 265167857Simp 266246128Ssbz DEVMETHOD_END 267167857Simp}; 268167857Simp 269167857Simpstatic driver_t icee_driver = { 270167857Simp "icee", 271167857Simp icee_methods, 272167857Simp sizeof(struct icee_softc), 273167857Simp}; 274167857Simpstatic devclass_t icee_devclass; 275167857Simp 276167857SimpDRIVER_MODULE(icee, iicbus, icee_driver, icee_devclass, 0, 0); 277167857SimpMODULE_VERSION(icee, 1); 278167857SimpMODULE_DEPEND(icee, iicbus, 1, 1, 1); 279