> On May 30, 2018, at 4:42 PM, Brad Spencer <b...@anduin.eldar.org> wrote:
> 
> Jason Thorpe <thor...@me.com> writes:
> 
>>> On May 30, 2018, at 11:54 AM, Brad Spencer <b...@anduin.eldar.org> wrote:
> 
> A zero length write would probably also work and should be just as safe,
> although I am not sure that every i2c controller supports that sort of
> thing.  The RPI had a KASSERT() that wasn't needed that would have
> paniced upon trying such a thing.  It was removed when the si70xx driver
> was added to the tree because that driver needed to be able to do that a
> zero length write, then wait before doing a read.  If clock stretching
> support existed in the i2c infrastructure, this would not have been
> required.

So, a “quick read” didn’t work… I had to actually read a byte back in order to 
get it to work correctly.

With just “quick read”:

[    1.000000] bsciic0 at fdt1: Broadcom Serial Controller
[    1.000000] iic0 at bsciic0: I2C bus
[    1.000000] dsrtc0 at iic0 addr 0x68: DS3231 Real-time Clock
[    1.000000] bsciic1 at fdt1: Broadcom Serial Controller
[    1.000000] iic1 at bsciic1: I2C bus
[    1.000000] dsrtc1 at iic1 addr 0x68: DS3231 Real-time Clock
[    1.000000] bsciic2 at fdt1: Broadcom Serial Controller
[    1.000000] iic2 at bsciic2: I2C bus
[    1.000000] dsrtc2 at iic2 addr 0x68: DS3231 Real-time Clock
[    1.000000] tsllux0 at iic2 addr 0x39: TSL256x Light-to-Digital converter

Note that the stupid clock still ghosts, but the light sensor does not, because 
the light sensor driver contains an additional “read byte” check (that’s 
actually a bit more dangerous potentially than a receive byte… but the intent 
is to read the Id register after verifying that’s it’s one of the legal 
addresses).  Both have wild carded parent locators.

With a “receive byte” instead:

[    1.000000] bsciic0 at fdt1: Broadcom Serial Controller
[    1.000000] iic0 at bsciic0: I2C bus
[    1.000000] bsciic1 at fdt1: Broadcom Serial Controller
[    1.000000] iic1 at bsciic1: I2C bus
[    1.000000] bsciic2 at fdt1: Broadcom Serial Controller
[    1.000000] iic2 at bsciic2: I2C bus
[    1.000000] dsrtc0 at iic2 addr 0x68: DS3231 Real-time Clock
[    1.000000] tsllux0 at iic2 addr 0x39: TSL256x Light-to-Digital converter

…i.e. the right thing happened.

NOW.  i2c_scan defaults to doing a “quick write”, HOWEVER, that code has the 
comment about the “quick write” method corrupting the EEPROM on Thinkpads.  But 
it also has a comment about the “receive byte” method possibly causing problems 
with write-only devices?

So, I’m a little perplexed about what to do… perhaps the “receive byte” method 
is the best for now?

? pca9685.c
? pca9685reg.h
Index: i2c.c
===================================================================
RCS file: /cvsroot/src/sys/dev/i2c/i2c.c,v
retrieving revision 1.58
diff -u -p -r1.58 i2c.c
--- i2c.c       15 May 2018 02:02:18 -0000      1.58
+++ i2c.c       31 May 2018 03:26:23 -0000
@@ -127,22 +127,51 @@ iic_print(void *aux, const char *pnp)
        return UNCONF;
 }
 
+static bool
+iic_is_special_address(i2c_addr_t addr)
+{
+
+       /*
+        * See: https://www.i2c-bus.org/addressing/
+        */
+
+       /* General Call (read) / Start Byte (write) */
+       if (addr == 0x00)
+               return (true);
+
+       /* CBUS Addresses */
+       if (addr == 0x01)
+               return (true);
+
+       /* Reserved for Different Bus Formats */
+       if (addr == 0x02)
+               return (true);
+
+       /* Reserved for future purposes */
+       if (addr == 0x03)
+               return (true);
+
+       /* High Speed Master Code */
+       if ((addr & 0x7c) == 0x04)
+               return (true);
+
+       /* 10-bit Slave Addressing prefix */
+       if ((addr & 0x7c) == 0x78)
+               return (true);
+       
+       /* Reserved for future purposes */
+       if ((addr & 0x7c) == 0x7c)
+               return (true);
+       
+       return (false);
+}
+
 static int
 iic_search(device_t parent, cfdata_t cf, const int *ldesc, void *aux)
 {
        struct iic_softc *sc = device_private(parent);
        struct i2c_attach_args ia;
 
-       /*
-        * I2C doesn't have any regular probing capability.  If we
-        * encounter a cfdata with a wild-carded address or a wild-
-        * carded parent spec, we skip them because they can only
-        * be used for direct-coniguration.
-        */
-       if (cf->cf_loc[IICCF_ADDR] == IICCF_ADDR_DEFAULT ||
-           cf->cf_pspec->cfp_unit == DVUNIT_ANY)
-               return 0;
-
        ia.ia_tag = sc->sc_tag;
        ia.ia_size = cf->cf_loc[IICCF_SIZE];
        ia.ia_type = sc->sc_type;
@@ -153,10 +182,43 @@ iic_search(device_t parent, cfdata_t cf,
        ia.ia_prop = NULL;
 
        for (ia.ia_addr = 0; ia.ia_addr <= I2C_MAX_ADDR; ia.ia_addr++) {
+               uint8_t dummy;
+               int error;
+
+               /*
+                * Skip I2C addresses that are reserved for
+                * special purposes.
+                */
+               if (iic_is_special_address(ia.ia_addr))
+                       continue;
+
                if (sc->sc_devices[ia.ia_addr] != NULL)
                        continue;
 
-               if (cf->cf_loc[IICCF_ADDR] != ia.ia_addr)
+               if (cf->cf_loc[IICCF_ADDR] != IICCF_ADDR_DEFAULT &&
+                   cf->cf_loc[IICCF_ADDR] != ia.ia_addr)
+                       continue;
+
+               /*
+                * Check for the presence of a device by trying
+                * to solicit an ACK in response to the address.
+                * In a perfect world, a "quick read" would do the
+                * trick, but it seems insufficient.  A "receive
+                * byte" seems more reliable, but potentially has
+                * problems with write-only devices?  A "quick
+                * write" might be the perfect solution if not
+                * for a known problem corrupting an EEPROM on
+                * some Thinkpad models, so we'll compromise with
+                * "receive byte" for now.
+                */
+               if ((error = iic_acquire_bus(ia.ia_tag, I2C_F_POLL)) != 0) {
+                       /* XXX log error?  bail out? */
+                       continue;
+               }
+               error = iic_smbus_receive_byte(ia.ia_tag, ia.ia_addr, &dummy,
+                                              I2C_F_POLL);
+               (void) iic_release_bus(ia.ia_tag, I2C_F_POLL);
+               if (error)
                        continue;
 
                if (config_match(parent, cf, &ia) > 0)

-- thorpej


Reply via email to