tech-kern archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: i2c and indirect vs. direct config




> On May 30, 2018, at 4:42 PM, Brad Spencer <brad%anduin.eldar.org@localhost> wrote:
> 
> Jason Thorpe <thorpej%me.com@localhost> writes:
> 
>>> On May 30, 2018, at 11:54 AM, Brad Spencer <brad%anduin.eldar.org@localhost> 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




Home | Main Index | Thread Index | Old Index