Author: ian
Date: Wed Jul 26 21:06:26 2017
New Revision: 321584
URL: https://svnweb.freebsd.org/changeset/base/321584

Log:
  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.
  
  Without support for nested request/release, slave device drivers would have
  to stop using high-level convenience functions and resort to working with
  arrays of iic_msg structs just for a few operations (often involving
  one-time device setup or infrequent configuration changes).
  
  The changes here appear large from a glance at the diff, but in fact they're
  nearly trivial, and the large diff is because of changes in indentation and
  the re-wrapping of comments caused by that.  One notable change is that
  iicbus_release_bus() now ignores the IICBUS_CALLBACK(IIC_RELEASE_BUS) return
  value.  The old error handling left the bus in a kind of limbo state where
  it was still owned at the iicbus layer, but drivers rarely check the return
  of the release call, and it's unclear what they would do to recover from an
  error return anyway.  No existing low-level drivers return any kind of error
  from IIC_RELEASE_BUS except one EINVAL for "you don't own the bus", to which
  the right response is probably to carry on with the process of releasing the
  reference to the bus anyway.

Modified:
  head/sys/dev/iicbus/iicbus.h
  head/sys/dev/iicbus/iiconf.c

Modified: head/sys/dev/iicbus/iicbus.h
==============================================================================
--- head/sys/dev/iicbus/iicbus.h        Wed Jul 26 20:40:24 2017        
(r321583)
+++ head/sys/dev/iicbus/iicbus.h        Wed Jul 26 21:06:26 2017        
(r321584)
@@ -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: head/sys/dev/iicbus/iiconf.c
==============================================================================
--- head/sys/dev/iicbus/iiconf.c        Wed Jul 26 20:40:24 2017        
(r321583)
+++ head/sys/dev/iicbus/iiconf.c        Wed Jul 26 21:06:26 2017        
(r321584)
@@ -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);
 }
 
 /*
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to