svn commit: r350874 - stable/12/sys/dev/iicbus

Ian Lepore ian at FreeBSD.org
Sun Aug 11 22:19:54 UTC 2019


Author: ian
Date: Sun Aug 11 22:19:54 2019
New Revision: 350874
URL: https://svnweb.freebsd.org/changeset/base/350874

Log:
  MFC r350015-r350016
  
  r350015:
  Fix nxprtc(4) on systems that support i2c repeat-start correctly.
  
  An obscure footnote in the datasheets for the PCx2127, PCx2129, and
  PCF8523 rtc chips states that the chips do not support i2c repeat-start
  operations.  When the driver was originally written and tested, the i2c
  bus on that system also didn't support repeat-start and just quietly
  turned repeat-start operations into a stop-then-start, making it appear
  that the nxprtc driver was working properly.
  
  The repeat-start situation only comes up on reads, so instead of using
  the standard iicdev_readfrom(), use a local nxprtc_readfrom(), which is
  just a cut-and-pasted copy of iicdev_readfrom(), modified to send two
  separate start-data-stop sequences instead of using repeat-start.
  
  r350016:
  In nxprtc(4), use the countdown timer for better timekeeping resolution
  on PCx2129 chips too.
  
  The datasheet for the PCx2129 chips says that there is only a watchdog
  timer, no countdown timer.  It turns out the countdown timer hardware is
  there and works just the same as it does on a PCx2127 chip, except that you
  can't use it to trigger an interrupt or toggle an output pin.  We don't need
  interrupts or output pins, we only need to read the timer register to get
  sub-second resolution.  So start treating the 2129 chips the same as 2127.

Modified:
  stable/12/sys/dev/iicbus/nxprtc.c
Directory Properties:
  stable/12/   (props changed)

Modified: stable/12/sys/dev/iicbus/nxprtc.c
==============================================================================
--- stable/12/sys/dev/iicbus/nxprtc.c	Sun Aug 11 22:11:59 2019	(r350873)
+++ stable/12/sys/dev/iicbus/nxprtc.c	Sun Aug 11 22:19:54 2019	(r350874)
@@ -33,8 +33,8 @@ __FBSDID("$FreeBSD$");
  *  - PCA8565 = like PCF8563, automotive temperature range
  *  - PCF8523 = low power, countdown timer, oscillator freq tuning, 2 timers
  *  - PCF2127 = like PCF8523, industrial, tcxo, tamper/ts, i2c & spi, 512B ram
- *  - PCA2129 = like PCF8523, automotive, tcxo, tamper/ts, i2c & spi, no timer
- *  - PCF2129 = like PCF8523, industrial, tcxo, tamper/ts, i2c & spi, no timer
+ *  - PCA2129 = like PCF8523, automotive, tcxo, tamper/ts, i2c & spi, (note 1)
+ *  - PCF2129 = like PCF8523, industrial, tcxo, tamper/ts, i2c & spi, (note 1)
  *
  *  Most chips have a countdown timer, ostensibly intended to generate periodic
  *  interrupt signals on an output pin.  The timer is driven from the same
@@ -42,6 +42,13 @@ __FBSDID("$FreeBSD$");
  *  in sync when the STOP bit is cleared after the time and timer registers are
  *  set.  The timer register can also be read on the fly, so we use it to count
  *  fractional seconds and get a resolution of ~15ms.
+ *
+ *  [1] Note that the datasheets for the PCx2129 chips state that they have only
+ *  a watchdog timer, not a countdown timer.  Empirical testing shows that the
+ *  countdown timer is actually there and it works fine, except that it can't
+ *  trigger an interrupt or toggle an output pin like it can on other chips.  We
+ *  don't care about interrupts and output pins, we just read the timer register
+ *  to get better resolution.
  */
 
 #include "opt_platform.h"
@@ -236,10 +243,43 @@ static nxprtc_compat_data compat_data[] = {
 };
 
 static int
+nxprtc_readfrom(device_t slavedev, uint8_t regaddr, void *buffer,
+    uint16_t buflen, int waithow)
+{
+	struct iic_msg msg;
+	int err;
+	uint8_t slaveaddr;
+
+	/*
+	 * Two transfers back to back with a stop and start between them; first we
+	 * write the address-within-device, then we read from the device.  This
+	 * is used instead of the standard iicdev_readfrom() because some of the
+	 * chips we service don't support i2c repeat-start operations (grrrrr)
+	 * so we do two completely separate transfers with a full stop between.
+	 */
+	slaveaddr = iicbus_get_addr(slavedev);
+
+	msg.slave = slaveaddr;
+	msg.flags = IIC_M_WR;
+	msg.len   = 1;
+	msg.buf   = ®addr;
+
+	if ((err = iicbus_transfer_excl(slavedev, &msg, 1, waithow)) != 0)
+		return (err);
+
+	msg.slave = slaveaddr;
+	msg.flags = IIC_M_RD;
+	msg.len   = buflen;
+	msg.buf   = buffer;
+
+	return (iicbus_transfer_excl(slavedev, &msg, 1, waithow));
+}
+
+static int
 read_reg(struct nxprtc_softc *sc, uint8_t reg, uint8_t *val)
 {
 
-	return (iicdev_readfrom(sc->dev, reg, val, sizeof(*val), WAITFLAGS));
+	return (nxprtc_readfrom(sc->dev, reg, val, sizeof(*val), WAITFLAGS));
 }
 
 static int
@@ -272,7 +312,7 @@ read_timeregs(struct nxprtc_softc *sc, struct time_reg
 			if (tmr1 != tmr2)
 				continue;
 		}
-		if ((err = iicdev_readfrom(sc->dev, sc->secaddr, tregs,
+		if ((err = nxprtc_readfrom(sc->dev, sc->secaddr, tregs,
 		    sizeof(*tregs), WAITFLAGS)) != 0)
 			break;
 	} while (sc->use_timer && tregs->sec != sec);
@@ -438,7 +478,13 @@ pcf2127_start_timer(struct nxprtc_softc *sc)
 	int err;
 	uint8_t stdctl, tmrctl;
 
-	/* See comment in pcf8523_start_timer().  */
+	/*
+	 * Set up timer if it's not already in the mode we normally run it.  See
+	 * the comment in pcf8523_start_timer() for more details.
+	 *
+	 * Note that the PCF2129 datasheet says it has no countdown timer, but
+	 * empirical testing shows that it works just fine for our purposes.
+	 */
 	if ((err = read_reg(sc, PCF2127_R_TMR_CTL, &tmrctl)) != 0)
 		return (err);
 
@@ -488,10 +534,6 @@ nxprtc_start(void *dev)
 	switch (sc->chiptype) {
 	case TYPE_PCA2129:
 	case TYPE_PCF2129:
-		if (pcf8523_start(sc) != 0)
-			return;
-		/* No timer to start */
-		break;
 	case TYPE_PCF2127:
 		if (pcf8523_start(sc) != 0)
 			return;
@@ -763,10 +805,6 @@ nxprtc_attach(device_t dev)
 	switch (sc->chiptype) {
 	case TYPE_PCA2129:
 	case TYPE_PCF2129:
-		sc->secaddr = PCF8523_R_SECOND;
-		sc->tmcaddr = 0;
-		sc->use_timer = false;
-		break;
 	case TYPE_PCF2127:
 	case TYPE_PCF8523:
 		sc->secaddr = PCF8523_R_SECOND;


More information about the svn-src-all mailing list