Date: Mon, 11 Sep 2017 17:01:27 +0000 (UTC) From: Ian Lepore <ian@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-11@freebsd.org Subject: svn commit: r323446 - stable/11/sys/dev/iicbus Message-ID: <201709111701.v8BH1RKP030583@repo.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: ian Date: Mon Sep 11 17:01:26 2017 New Revision: 323446 URL: https://svnweb.freebsd.org/changeset/base/323446 Log: MFC r321583, r321584: Add a pair of convenience routines for doing simple "register" read/writes on i2c devices, where the "register" can be any length. Add support for tracking nested calls to iicbus_request/release_bus(). Usually it is sufficient to use iicbus_transfer_excl(), or one of the higher-level convenience functions that use it, to reserve the bus for the duration of each register access. Occasionally it is important that a series of accesses or read-modify-write operations must be done without any other intervening access to the device, to prevent corrupting state. Modified: stable/11/sys/dev/iicbus/iicbus.h stable/11/sys/dev/iicbus/iiconf.c stable/11/sys/dev/iicbus/iiconf.h Directory Properties: stable/11/ (props changed) Modified: stable/11/sys/dev/iicbus/iicbus.h ============================================================================== --- stable/11/sys/dev/iicbus/iicbus.h Mon Sep 11 15:59:20 2017 (r323445) +++ stable/11/sys/dev/iicbus/iicbus.h Mon Sep 11 17:01:26 2017 (r323446) @@ -39,6 +39,7 @@ struct iicbus_softc { device_t dev; /* Myself */ device_t owner; /* iicbus owner device structure */ + u_int owncount; /* iicbus ownership nesting count */ u_char started; /* address of the 'started' slave * 0 if no start condition succeeded */ u_char strict; /* deny operations that violate the Modified: stable/11/sys/dev/iicbus/iiconf.c ============================================================================== --- stable/11/sys/dev/iicbus/iiconf.c Mon Sep 11 15:59:20 2017 (r323445) +++ stable/11/sys/dev/iicbus/iiconf.c Mon Sep 11 17:01:26 2017 (r323446) @@ -113,26 +113,30 @@ iicbus_request_bus(device_t bus, device_t dev, int how IICBUS_LOCK(sc); - while ((error == 0) && (sc->owner != NULL)) + while (error == 0 && sc->owner != NULL && sc->owner != dev) error = iicbus_poll(sc, how); if (error == 0) { - sc->owner = dev; - /* - * Drop the lock around the call to the bus driver. - * This call should be allowed to sleep in the IIC_WAIT case. - * Drivers might also need to grab locks that would cause LOR - * if our lock is held. - */ - IICBUS_UNLOCK(sc); - /* Ask the underlying layers if the request is ok */ - error = IICBUS_CALLBACK(device_get_parent(bus), - IIC_REQUEST_BUS, (caddr_t)&how); - IICBUS_LOCK(sc); - - if (error != 0) { - sc->owner = NULL; - wakeup_one(sc); + ++sc->owncount; + if (sc->owner == NULL) { + sc->owner = dev; + /* + * Drop the lock around the call to the bus driver, it + * should be allowed to sleep in the IIC_WAIT case. + * Drivers might also need to grab locks that would + * cause a LOR if our lock is held. + */ + IICBUS_UNLOCK(sc); + /* Ask the underlying layers if the request is ok */ + error = IICBUS_CALLBACK(device_get_parent(bus), + IIC_REQUEST_BUS, (caddr_t)&how); + IICBUS_LOCK(sc); + + if (error != 0) { + sc->owner = NULL; + sc->owncount = 0; + wakeup_one(sc); + } } } @@ -150,7 +154,6 @@ int iicbus_release_bus(device_t bus, device_t dev) { struct iicbus_softc *sc = (struct iicbus_softc *)device_get_softc(bus); - int error; IICBUS_LOCK(sc); @@ -159,26 +162,16 @@ iicbus_release_bus(device_t bus, device_t dev) return (IIC_EBUSBSY); } - /* - * Drop the lock around the call to the bus driver. - * This call should be allowed to sleep in the IIC_WAIT case. - * Drivers might also need to grab locks that would cause LOR - * if our lock is held. - */ - IICBUS_UNLOCK(sc); - /* Ask the underlying layers if the release is ok */ - error = IICBUS_CALLBACK(device_get_parent(bus), IIC_RELEASE_BUS, NULL); - - if (error == 0) { + if (--sc->owncount == 0) { + /* Drop the lock while informing the low-level driver. */ + IICBUS_UNLOCK(sc); + IICBUS_CALLBACK(device_get_parent(bus), IIC_RELEASE_BUS, NULL); IICBUS_LOCK(sc); sc->owner = NULL; - - /* wakeup a waiting thread */ wakeup_one(sc); - IICBUS_UNLOCK(sc); } - - return (error); + IICBUS_UNLOCK(sc); + return (0); } /* @@ -469,4 +462,56 @@ iicbus_transfer_gen(device_t dev, struct iic_msg *msgs if (error != 0 && started) iicbus_stop(bus); return (error); +} + +int +iicdev_readfrom(device_t slavedev, uint8_t regaddr, void *buffer, + uint16_t buflen, int waithow) +{ + struct iic_msg msgs[2]; + uint8_t slaveaddr; + + /* + * Two transfers back to back with a repeat-start between them; first we + * write the address-within-device, then we read from the device. + */ + slaveaddr = iicbus_get_addr(slavedev); + + msgs[0].slave = slaveaddr; + msgs[0].flags = IIC_M_WR | IIC_M_NOSTOP; + msgs[0].len = 1; + msgs[0].buf = ®addr; + + msgs[1].slave = slaveaddr; + msgs[1].flags = IIC_M_RD; + msgs[1].len = buflen; + msgs[1].buf = buffer; + + return (iicbus_transfer_excl(slavedev, msgs, nitems(msgs), waithow)); +} + +int iicdev_writeto(device_t slavedev, uint8_t regaddr, void *buffer, + uint16_t buflen, int waithow) +{ + struct iic_msg msgs[2]; + uint8_t slaveaddr; + + /* + * Two transfers back to back with no stop or start between them; first + * we write the address then we write the data to that address, all in a + * single transfer from two scattered buffers. + */ + slaveaddr = iicbus_get_addr(slavedev); + + msgs[0].slave = slaveaddr; + msgs[0].flags = IIC_M_WR | IIC_M_NOSTOP; + msgs[0].len = 1; + msgs[0].buf = ®addr; + + msgs[1].slave = slaveaddr; + msgs[1].flags = IIC_M_WR | IIC_M_NOSTART; + msgs[1].len = buflen; + msgs[1].buf = buffer; + + return (iicbus_transfer_excl(slavedev, msgs, nitems(msgs), waithow)); } Modified: stable/11/sys/dev/iicbus/iiconf.h ============================================================================== --- stable/11/sys/dev/iicbus/iiconf.h Mon Sep 11 15:59:20 2017 (r323445) +++ stable/11/sys/dev/iicbus/iiconf.h Mon Sep 11 17:01:26 2017 (r323446) @@ -133,6 +133,16 @@ int iicbus_transfer_excl(device_t bus, struct iic_msg int how); int iicbus_transfer_gen(device_t bus, struct iic_msg *msgs, uint32_t nmsgs); +/* + * Simple register read/write routines, but the "register" can be any size. + * The transfers are done with iicbus_transfer_excl(). Reads use a repeat-start + * between sending the address and reading; writes use a single start/stop. + */ +int iicdev_readfrom(device_t _slavedev, uint8_t _regaddr, void *_buffer, + uint16_t _buflen, int _waithow); +int iicdev_writeto(device_t _slavedev, uint8_t _regaddr, void *_buffer, + uint16_t _buflen, int _waithow); + #define IICBUS_MODVER 1 #define IICBUS_MINVER 1 #define IICBUS_MAXVER 1
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201709111701.v8BH1RKP030583>