138774Snsouch/*- 293023Snsouch * Copyright (c) 1998, 2001 Nicolas Souchu 338774Snsouch * All rights reserved. 438774Snsouch * 538774Snsouch * Redistribution and use in source and binary forms, with or without 638774Snsouch * modification, are permitted provided that the following conditions 738774Snsouch * are met: 838774Snsouch * 1. Redistributions of source code must retain the above copyright 938774Snsouch * notice, this list of conditions and the following disclaimer. 1038774Snsouch * 2. Redistributions in binary form must reproduce the above copyright 1138774Snsouch * notice, this list of conditions and the following disclaimer in the 1238774Snsouch * documentation and/or other materials provided with the distribution. 1338774Snsouch * 1438774Snsouch * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1538774Snsouch * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1638774Snsouch * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1738774Snsouch * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1838774Snsouch * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1938774Snsouch * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2038774Snsouch * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2138774Snsouch * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2238774Snsouch * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2338774Snsouch * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2438774Snsouch * SUCH DAMAGE. 2538774Snsouch * 2650477Speter * $FreeBSD$ 2738774Snsouch * 2838774Snsouch */ 2938774Snsouch#include <sys/param.h> 30181304Sjhb#include <sys/bus.h> 31181304Sjhb#include <sys/conf.h> 32181304Sjhb#include <sys/fcntl.h> 33181304Sjhb#include <sys/lock.h> 3438774Snsouch#include <sys/kernel.h> 35129291Sjoerg#include <sys/malloc.h> 3638774Snsouch#include <sys/module.h> 37181304Sjhb#include <sys/sx.h> 38181304Sjhb#include <sys/systm.h> 3938774Snsouch#include <sys/uio.h> 4038774Snsouch 4138774Snsouch#include <dev/iicbus/iiconf.h> 4238774Snsouch#include <dev/iicbus/iicbus.h> 43103588Speter#include <dev/iicbus/iic.h> 4442442Snsouch 4538774Snsouch#include "iicbus_if.h" 4638774Snsouch 4738774Snsouch#define BUFSIZE 1024 4838774Snsouch 4938774Snsouchstruct iic_softc { 5038774Snsouch 51181304Sjhb device_t sc_dev; 5293023Snsouch u_char sc_addr; /* 7 bit address on iicbus */ 5338774Snsouch int sc_count; /* >0 if device opened */ 5438774Snsouch 5538774Snsouch char sc_buffer[BUFSIZE]; /* output buffer */ 5638774Snsouch char sc_inbuf[BUFSIZE]; /* input buffer */ 5793023Snsouch 58130585Sphk struct cdev *sc_devnode; 59181304Sjhb struct sx sc_lock; 6038774Snsouch}; 6138774Snsouch 62181304Sjhb#define IIC_LOCK(sc) sx_xlock(&(sc)->sc_lock) 63181304Sjhb#define IIC_UNLOCK(sc) sx_xunlock(&(sc)->sc_lock) 6438774Snsouch 6538774Snsouchstatic int iic_probe(device_t); 6638774Snsouchstatic int iic_attach(device_t); 6793023Snsouchstatic int iic_detach(device_t); 6893023Snsouchstatic void iic_identify(driver_t *driver, device_t parent); 6938774Snsouch 7038774Snsouchstatic devclass_t iic_devclass; 7138774Snsouch 7238774Snsouchstatic device_method_t iic_methods[] = { 7338774Snsouch /* device interface */ 7493023Snsouch DEVMETHOD(device_identify, iic_identify), 7538774Snsouch DEVMETHOD(device_probe, iic_probe), 7638774Snsouch DEVMETHOD(device_attach, iic_attach), 7793023Snsouch DEVMETHOD(device_detach, iic_detach), 7838774Snsouch 7938774Snsouch /* iicbus interface */ 8038774Snsouch DEVMETHOD(iicbus_intr, iicbus_generic_intr), 8138774Snsouch 8238774Snsouch { 0, 0 } 8338774Snsouch}; 8438774Snsouch 8538774Snsouchstatic driver_t iic_driver = { 8638774Snsouch "iic", 8738774Snsouch iic_methods, 8838774Snsouch sizeof(struct iic_softc), 8938774Snsouch}; 9038774Snsouch 9138774Snsouchstatic d_open_t iicopen; 9238774Snsouchstatic d_close_t iicclose; 9338774Snsouchstatic d_write_t iicwrite; 9438774Snsouchstatic d_read_t iicread; 9538774Snsouchstatic d_ioctl_t iicioctl; 9638774Snsouch 9747625Sphkstatic struct cdevsw iic_cdevsw = { 98126080Sphk .d_version = D_VERSION, 99181304Sjhb .d_flags = D_TRACKCLOSE, 100111815Sphk .d_open = iicopen, 101111815Sphk .d_close = iicclose, 102111815Sphk .d_read = iicread, 103111815Sphk .d_write = iicwrite, 104111815Sphk .d_ioctl = iicioctl, 105111815Sphk .d_name = "iic", 10647625Sphk}; 10738774Snsouch 10893023Snsouchstatic void 10993023Snsouchiic_identify(driver_t *driver, device_t parent) 11093023Snsouch{ 111181304Sjhb 112181304Sjhb if (device_find_child(parent, "iic", -1) == NULL) 113187321Snwhitehorn BUS_ADD_CHILD(parent, 0, "iic", -1); 11493023Snsouch} 11593023Snsouch 11638774Snsouchstatic int 11738774Snsouchiic_probe(device_t dev) 11838774Snsouch{ 119187321Snwhitehorn if (iicbus_get_addr(dev) > 0) 120187321Snwhitehorn return (ENXIO); 121187321Snwhitehorn 12293023Snsouch device_set_desc(dev, "I2C generic I/O"); 123187321Snwhitehorn 124187321Snwhitehorn return (0); 12538774Snsouch} 12638774Snsouch 12738774Snsouchstatic int 12838774Snsouchiic_attach(device_t dev) 12938774Snsouch{ 13093023Snsouch struct iic_softc *sc = (struct iic_softc *)device_get_softc(dev); 13193023Snsouch 132181304Sjhb sc->sc_dev = dev; 133181304Sjhb sx_init(&sc->sc_lock, "iic"); 13493023Snsouch sc->sc_devnode = make_dev(&iic_cdevsw, device_get_unit(dev), 13553329Speter UID_ROOT, GID_WHEEL, 13653329Speter 0600, "iic%d", device_get_unit(dev)); 137181304Sjhb if (sc->sc_devnode == NULL) { 138181304Sjhb device_printf(dev, "failed to create character device\n"); 139181304Sjhb sx_destroy(&sc->sc_lock); 140181304Sjhb return (ENXIO); 141181304Sjhb } 142181304Sjhb sc->sc_devnode->si_drv1 = sc; 143181304Sjhb 14438774Snsouch return (0); 14538774Snsouch} 14638774Snsouch 14738774Snsouchstatic int 14893023Snsouchiic_detach(device_t dev) 14993023Snsouch{ 15093023Snsouch struct iic_softc *sc = (struct iic_softc *)device_get_softc(dev); 15193023Snsouch 15293023Snsouch if (sc->sc_devnode) 15393023Snsouch destroy_dev(sc->sc_devnode); 154181304Sjhb sx_destroy(&sc->sc_lock); 15593023Snsouch 15693023Snsouch return (0); 15793023Snsouch} 15893023Snsouch 15993023Snsouchstatic int 160157523Simpiicopen(struct cdev *dev, int flags, int fmt, struct thread *td) 16138774Snsouch{ 162181304Sjhb struct iic_softc *sc = dev->si_drv1; 16338774Snsouch 164181304Sjhb IIC_LOCK(sc); 165181304Sjhb if (sc->sc_count > 0) { 166181304Sjhb IIC_UNLOCK(sc); 16738774Snsouch return (EBUSY); 168181304Sjhb } 16938774Snsouch 17038774Snsouch sc->sc_count++; 171181304Sjhb IIC_UNLOCK(sc); 17238774Snsouch 17338774Snsouch return (0); 17438774Snsouch} 17538774Snsouch 17638774Snsouchstatic int 177130585Sphkiicclose(struct cdev *dev, int flags, int fmt, struct thread *td) 17838774Snsouch{ 179181304Sjhb struct iic_softc *sc = dev->si_drv1; 18038774Snsouch 181181304Sjhb IIC_LOCK(sc); 182181304Sjhb if (!sc->sc_count) { 183181304Sjhb /* XXX: I don't think this can happen. */ 184181304Sjhb IIC_UNLOCK(sc); 18538774Snsouch return (EINVAL); 186181304Sjhb } 18738774Snsouch 18838774Snsouch sc->sc_count--; 18938774Snsouch 19038774Snsouch if (sc->sc_count < 0) 19187599Sobrien panic("%s: iic_count < 0!", __func__); 192181304Sjhb IIC_UNLOCK(sc); 19338774Snsouch 19438774Snsouch return (0); 19538774Snsouch} 19638774Snsouch 19738774Snsouchstatic int 198130585Sphkiicwrite(struct cdev *dev, struct uio * uio, int ioflag) 19938774Snsouch{ 200181304Sjhb struct iic_softc *sc = dev->si_drv1; 201181304Sjhb device_t iicdev = sc->sc_dev; 20238774Snsouch int sent, error, count; 20338774Snsouch 204181304Sjhb IIC_LOCK(sc); 205181304Sjhb if (!sc->sc_addr) { 206181304Sjhb IIC_UNLOCK(sc); 20738774Snsouch return (EINVAL); 208181304Sjhb } 20938774Snsouch 210181304Sjhb if (sc->sc_count == 0) { 211181304Sjhb /* XXX: I don't think this can happen. */ 212181304Sjhb IIC_UNLOCK(sc); 21338774Snsouch return (EINVAL); 214181304Sjhb } 21538774Snsouch 216181304Sjhb error = iicbus_request_bus(device_get_parent(iicdev), iicdev, 217181304Sjhb IIC_DONTWAIT); 218181304Sjhb if (error) { 219181304Sjhb IIC_UNLOCK(sc); 22043976Snsouch return (error); 221181304Sjhb } 22243976Snsouch 22338774Snsouch count = min(uio->uio_resid, BUFSIZE); 224242947Skevlo error = uiomove(sc->sc_buffer, count, uio); 225242947Skevlo if (error) { 226242947Skevlo IIC_UNLOCK(sc); 227242947Skevlo return (error); 228242947Skevlo } 22938774Snsouch 23038774Snsouch error = iicbus_block_write(device_get_parent(iicdev), sc->sc_addr, 23138774Snsouch sc->sc_buffer, count, &sent); 23238774Snsouch 23343976Snsouch iicbus_release_bus(device_get_parent(iicdev), iicdev); 234181304Sjhb IIC_UNLOCK(sc); 23543976Snsouch 236181304Sjhb return (error); 23738774Snsouch} 23838774Snsouch 23938774Snsouchstatic int 240130585Sphkiicread(struct cdev *dev, struct uio * uio, int ioflag) 24138774Snsouch{ 242181304Sjhb struct iic_softc *sc = dev->si_drv1; 243181304Sjhb device_t iicdev = sc->sc_dev; 24438774Snsouch int len, error = 0; 24538774Snsouch int bufsize; 24638774Snsouch 247181304Sjhb IIC_LOCK(sc); 248181304Sjhb if (!sc->sc_addr) { 249181304Sjhb IIC_UNLOCK(sc); 25038774Snsouch return (EINVAL); 251181304Sjhb } 25238774Snsouch 253181304Sjhb if (sc->sc_count == 0) { 254181304Sjhb /* XXX: I don't think this can happen. */ 255181304Sjhb IIC_UNLOCK(sc); 25638774Snsouch return (EINVAL); 257181304Sjhb } 25838774Snsouch 259181304Sjhb error = iicbus_request_bus(device_get_parent(iicdev), iicdev, 260181304Sjhb IIC_DONTWAIT); 261181304Sjhb if (error) { 262181304Sjhb IIC_UNLOCK(sc); 26343976Snsouch return (error); 264181304Sjhb } 26543976Snsouch 26638774Snsouch /* max amount of data to read */ 26738774Snsouch len = min(uio->uio_resid, BUFSIZE); 26838774Snsouch 269181304Sjhb error = iicbus_block_read(device_get_parent(iicdev), sc->sc_addr, 270181304Sjhb sc->sc_inbuf, len, &bufsize); 271181304Sjhb if (error) { 272181304Sjhb IIC_UNLOCK(sc); 27338774Snsouch return (error); 274181304Sjhb } 27538774Snsouch 27638774Snsouch if (bufsize > uio->uio_resid) 27787599Sobrien panic("%s: too much data read!", __func__); 27838774Snsouch 27943976Snsouch iicbus_release_bus(device_get_parent(iicdev), iicdev); 28043976Snsouch 281181304Sjhb error = uiomove(sc->sc_inbuf, bufsize, uio); 282181304Sjhb IIC_UNLOCK(sc); 283181304Sjhb return (error); 28438774Snsouch} 28538774Snsouch 28638774Snsouchstatic int 287130585Sphkiicioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags, struct thread *td) 28838774Snsouch{ 289181304Sjhb struct iic_softc *sc = dev->si_drv1; 290181304Sjhb device_t iicdev = sc->sc_dev; 29138774Snsouch device_t parent = device_get_parent(iicdev); 29242442Snsouch struct iiccmd *s = (struct iiccmd *)data; 293160372Simp struct iic_rdwr_data *d = (struct iic_rdwr_data *)data; 294160372Simp struct iic_msg *m; 295160372Simp int error, count, i; 296129291Sjoerg char *buf = NULL; 297160372Simp void **usrbufs = NULL; 29838774Snsouch 299167856Simp if ((error = iicbus_request_bus(parent, iicdev, 300167856Simp (flags & O_NONBLOCK) ? IIC_DONTWAIT : (IIC_WAIT | IIC_INTR)))) 30143976Snsouch return (error); 30243976Snsouch 30338774Snsouch switch (cmd) { 30438774Snsouch case I2CSTART: 305181304Sjhb IIC_LOCK(sc); 30642442Snsouch error = iicbus_start(parent, s->slave, 0); 30793023Snsouch 30893023Snsouch /* 30993023Snsouch * Implicitly set the chip addr to the slave addr passed as 31093023Snsouch * parameter. Consequently, start/stop shall be called before 31193023Snsouch * the read or the write of a block. 31293023Snsouch */ 31393023Snsouch if (!error) 31493023Snsouch sc->sc_addr = s->slave; 315181304Sjhb IIC_UNLOCK(sc); 31693023Snsouch 31738774Snsouch break; 31838774Snsouch 31938774Snsouch case I2CSTOP: 32038774Snsouch error = iicbus_stop(parent); 32138774Snsouch break; 32238774Snsouch 32338774Snsouch case I2CRSTCARD: 324157482Simp error = iicbus_reset(parent, IIC_UNKNOWN, 0, NULL); 325270241Sloos /* 326270241Sloos * Ignore IIC_ENOADDR as it only means we have a master-only 327270241Sloos * controller. 328270241Sloos */ 329270241Sloos if (error == IIC_ENOADDR) 330270241Sloos error = 0; 33138774Snsouch break; 33238774Snsouch 33342442Snsouch case I2CWRITE: 334129291Sjoerg if (s->count <= 0) { 335129291Sjoerg error = EINVAL; 336129291Sjoerg break; 337129291Sjoerg } 338129291Sjoerg buf = malloc((unsigned long)s->count, M_TEMP, M_WAITOK); 339129291Sjoerg error = copyin(s->buf, buf, s->count); 340129291Sjoerg if (error) 341129291Sjoerg break; 342129291Sjoerg error = iicbus_write(parent, buf, s->count, &count, 10); 34342442Snsouch break; 34442442Snsouch 34542442Snsouch case I2CREAD: 346129291Sjoerg if (s->count <= 0) { 347129291Sjoerg error = EINVAL; 348129291Sjoerg break; 349129291Sjoerg } 350129291Sjoerg buf = malloc((unsigned long)s->count, M_TEMP, M_WAITOK); 351129291Sjoerg error = iicbus_read(parent, buf, s->count, &count, s->last, 10); 352129291Sjoerg if (error) 353129291Sjoerg break; 354129291Sjoerg error = copyout(buf, s->buf, s->count); 35542442Snsouch break; 35642442Snsouch 357160372Simp case I2CRDWR: 358160372Simp buf = malloc(sizeof(*d->msgs) * d->nmsgs, M_TEMP, M_WAITOK); 359160372Simp error = copyin(d->msgs, buf, sizeof(*d->msgs) * d->nmsgs); 360226442Sbrueffer if (error) 361160372Simp break; 362164501Simp /* Alloc kernel buffers for userland data, copyin write data */ 363226442Sbrueffer usrbufs = malloc(sizeof(void *) * d->nmsgs, M_TEMP, M_ZERO | M_WAITOK); 364160372Simp for (i = 0; i < d->nmsgs; i++) { 365160372Simp m = &((struct iic_msg *)buf)[i]; 366160372Simp usrbufs[i] = m->buf; 367160372Simp m->buf = malloc(m->len, M_TEMP, M_WAITOK); 368160372Simp if (!(m->flags & IIC_M_RD)) 369160372Simp copyin(usrbufs[i], m->buf, m->len); 370160372Simp } 371167856Simp error = iicbus_transfer(iicdev, (struct iic_msg *)buf, d->nmsgs); 372160372Simp /* Copyout all read segments, free up kernel buffers */ 373160372Simp for (i = 0; i < d->nmsgs; i++) { 374160372Simp m = &((struct iic_msg *)buf)[i]; 375164501Simp if (m->flags & IIC_M_RD) 376160372Simp copyout(m->buf, usrbufs[i], m->len); 377160372Simp free(m->buf, M_TEMP); 378160372Simp } 379160372Simp free(usrbufs, M_TEMP); 380160372Simp break; 381187709Sraj 382187709Sraj case I2CRPTSTART: 383187709Sraj error = iicbus_repeated_start(parent, s->slave, 0); 384187709Sraj break; 385187709Sraj 38638774Snsouch default: 387129291Sjoerg error = ENOTTY; 38838774Snsouch } 38938774Snsouch 390167856Simp iicbus_release_bus(parent, iicdev); 39143976Snsouch 392129291Sjoerg if (buf != NULL) 393129291Sjoerg free(buf, M_TEMP); 39438774Snsouch return (error); 39538774Snsouch} 39638774Snsouch 39753005SpeterDRIVER_MODULE(iic, iicbus, iic_driver, iic_devclass, 0, 0); 39893023SnsouchMODULE_DEPEND(iic, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER); 39993023SnsouchMODULE_VERSION(iic, 1); 400