Index: sys/dev/i2c/lm75.c
===================================================================
RCS file: /cvsroot/src/sys/dev/i2c/lm75.c,v
retrieving revision 1.36
diff -u -r1.36 lm75.c
--- sys/dev/i2c/lm75.c	21 Aug 2020 20:41:43 -0000	1.36
+++ sys/dev/i2c/lm75.c	7 Dec 2020 15:00:25 -0000
@@ -201,7 +201,8 @@
 	sc->sc_lmtemp_decode = lmtemptbl[i].lmtemp_decode;
 	sc->sc_lmtemp_encode = lmtemptbl[i].lmtemp_encode;
 
-	iic_acquire_bus(sc->sc_tag, 0);
+	if (iic_acquire_bus(sc->sc_tag, 0))
+		return;
 
 	/* Read temperature limit(s) and remember initial value(s). */
 	if (i == lmtemp_lm77) {
@@ -349,9 +350,10 @@
 {
 	struct lmtemp_softc *sc = sme->sme_cookie;
 
-	iic_acquire_bus(sc->sc_tag, 0);	/* also locks our instance */
+	if (iic_acquire_bus(sc->sc_tag, 0))	/* also locks our instance */
+		return;
 	lmtemp_refresh_sensor_data(sc);
-	iic_release_bus(sc->sc_tag, 0);	/* also unlocks our instance */
+	iic_release_bus(sc->sc_tag, 0); 	/* also unlocks our instance */
 }
 
 static void
@@ -363,7 +365,8 @@
 
 	*props &= ~(PROP_CRITMAX);
 
-	iic_acquire_bus(sc->sc_tag, 0);
+	if (iic_acquire_bus(sc->sc_tag, 0))
+		return;
 	if (lmtemp_temp_read(sc, LM75_REG_TOS_SET_POINT, &val, 0) == 0) {
 		limits->sel_critmax = val;
 		*props |= PROP_CRITMAX;
@@ -380,7 +383,8 @@
 
 	*props &= ~(PROP_CRITMAX | PROP_WARNMAX | PROP_WARNMIN);
 
-	iic_acquire_bus(sc->sc_tag, 0);
+	if (iic_acquire_bus(sc->sc_tag, 0))
+		return;
 	if (lmtemp_temp_read(sc, LM77_REG_TCRIT_SET_POINT, &val, 0) == 0) {
 		limits->sel_critmax = val;
 		*props |= PROP_CRITMAX;
@@ -408,7 +412,8 @@
 			limit = sc->sc_smax;
 		else
 			limit = limits->sel_critmax;
-		iic_acquire_bus(sc->sc_tag, 0);
+		if (iic_acquire_bus(sc->sc_tag, 0))
+			return;
 		lmtemp_temp_write(sc, LM75_REG_THYST_SET_POINT,
 		    limit - 5000000, 0);
 		lmtemp_temp_write(sc, LM75_REG_TOS_SET_POINT, limit, 0);
@@ -426,7 +431,8 @@
 	struct lmtemp_softc *sc = sme->sme_cookie;
 	int32_t limit;
 
-	iic_acquire_bus(sc->sc_tag, 0);
+	if (iic_acquire_bus(sc->sc_tag, 0))
+		return;
 	if (*props & PROP_CRITMAX) {
 		if (limits == NULL)	/* Restore defaults */
 			limit = sc->sc_scrit;
@@ -589,7 +595,8 @@
 
 			temp = *(int *)node.sysctl_data;
 			sc->sc_tmax = temp;
-			iic_acquire_bus(sc->sc_tag, 0);
+			if (iic_acquire_bus(sc->sc_tag, 0))
+				return EBUSY;
 			lmtemp_temp_write(sc, LM75_REG_THYST_SET_POINT,
 			    sc->sc_tmax - 5, 1);
 			lmtemp_temp_write(sc, LM75_REG_TOS_SET_POINT,
Index: sys/arch/sparc64/dev/pcf8591_envctrl.c
===================================================================
RCS file: /cvsroot/src/sys/arch/sparc64/dev/pcf8591_envctrl.c,v
retrieving revision 1.14
diff -u -r1.14 pcf8591_envctrl.c
--- sys/arch/sparc64/dev/pcf8591_envctrl.c	7 Dec 2020 13:24:15 -0000	1.14
+++ sys/arch/sparc64/dev/pcf8591_envctrl.c	7 Dec 2020 15:00:25 -0000
@@ -55,7 +55,6 @@
 
 #define PCF8591_TEMP_SENS	0x00
 #define PCF8591_CPU_FAN_CTRL	0x01
-#define PCF8591_PS_FAN_CTRL	0x02
 
 struct ecadc_channel {
 	u_int		chan_num;
@@ -89,6 +88,7 @@
 static void	ecadc_refresh(struct sysmon_envsys *, envsys_data_t *);
 static void	ecadc_get_limits(struct sysmon_envsys *, envsys_data_t *,
 			sysmon_envsys_lim_t *, u_int32_t *);
+static int	ecadc_set_fan_speed(struct ecadc_softc *, u_int8_t, u_int8_t);
 static void	ecadc_timeout(void *);
 static void	ecadc_fan_adjust(void *);
 
@@ -205,6 +205,21 @@
 		sc->sc_nchan++;
 	}
 
+	sc->sc_tag = ia->ia_tag;
+	sc->sc_addr = ia->ia_addr;
+
+	iic_acquire_bus(sc->sc_tag, 0);
+
+	/* Try a read now, so we can fail if this component isn't present */
+	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
+	    NULL, 0, junk, sc->sc_nchan + 1, 0)) {
+		aprint_normal(": read failed\n");
+		iic_release_bus(sc->sc_tag, 0);
+		return;
+	}
+
+	iic_release_bus(sc->sc_tag, 0);
+
 	/*
 	 * Fan speed changing information is missing from OFW
 	 * The E250 CPU fan is connected to the sensor at addr 0x4a, channel 1
@@ -214,7 +229,6 @@
 	    XLATE_MAX) > 0) {
 		sc->sc_channels[sc->sc_nchan].chan_num = 1;
 		sc->sc_channels[sc->sc_nchan].chan_type = PCF8591_CPU_FAN_CTRL;
-		sc->sc_channels[sc->sc_nchan].chan_speed = 0;
 		sensor = &sc->sc_channels[sc->sc_nchan].chan_sensor;
 		sensor->units = ENVSYS_INTEGER;
 		sensor->flags = ENVSYS_FMONNOTSUPP;
@@ -225,26 +239,17 @@
 		    "added CPUFAN sensor (chan %d) with cpu-fan xlate\n",
 		    device_xname(sc->sc_dev),
 		    sc->sc_channels[sc->sc_nchan].chan_num);
-		sc->sc_nchan++;
 
-		sc->sc_hastimer = 1;
-	}
+		/* Set the fan to medium speed */
+		sc->sc_channels[sc->sc_nchan].chan_speed =
+		    (sc->sc_cpu_fan_spd[0]+sc->sc_cpu_fan_spd[XLATE_MAX])/2;
+		ecadc_set_fan_speed(sc, sc->sc_channels[sc->sc_nchan].chan_num,
+		    sc->sc_channels[sc->sc_nchan].chan_speed);
 
-	sc->sc_tag = ia->ia_tag;
-	sc->sc_addr = ia->ia_addr;
-
-	iic_acquire_bus(sc->sc_tag, 0);
-
-	/* Try a read now, so we can fail if this component isn't present */
-	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
-	    NULL, 0, junk, sc->sc_nchan + 1, 0)) {
-		aprint_normal(": read failed\n");
-		iic_release_bus(sc->sc_tag, 0);
-		return;
+		sc->sc_nchan++;
+		sc->sc_hastimer = 1;
 	}
 
-	iic_release_bus(sc->sc_tag, 0);
-
 	/* Hook us into the sysmon_envsys subsystem */
 	sc->sc_sme = sysmon_envsys_create();
 	sc->sc_sme->sme_name = device_xname(self);
@@ -280,6 +285,8 @@
 ecadc_detach(device_t self, int flags)
 {
 	struct ecadc_softc *sc = device_private(self);
+	int c, i;
+
 	if (sc->sc_hastimer) {
 		callout_halt(&sc->sc_timer, NULL);
 		callout_destroy(&sc->sc_timer);
@@ -288,6 +295,23 @@
 	if (sc->sc_sme != NULL)
 		sysmon_envsys_unregister(sc->sc_sme);
 
+	for (i = 0; i < sc->sc_nchan; i++) {
+		struct ecadc_channel *chp = &sc->sc_channels[i];
+
+		if (chp->chan_type == PCF8591_CPU_FAN_CTRL) {
+			/* Loop in case the bus is busy */
+			for (c = 0; c < 5; c++) {
+				chp->chan_speed = sc->sc_cpu_fan_spd[0];
+				if (!ecadc_set_fan_speed(sc, chp->chan_num,
+				    chp->chan_speed))
+					return 0;
+				delay(10000);
+			}
+			aprint_error_dev(sc->sc_dev,
+			    "cannot set fan speed (chan %d)\n", chp->chan_num);
+		}
+	}
+
 	return 0;
 }
 
@@ -350,8 +374,7 @@
 			}
 			chp->chan_sensor.flags |= ENVSYS_FMONLIMITS;
 		}
-		if (chp->chan_type == PCF8591_CPU_FAN_CTRL ||
-		    chp->chan_type == PCF8591_PS_FAN_CTRL)
+		if (chp->chan_type == PCF8591_CPU_FAN_CTRL)
 			chp->chan_sensor.value_cur = data[1 + chp->chan_num];
 
 		chp->chan_sensor.state = ENVSYS_SVALID;
Index: sys/arch/sparc64/dev/tda.c
===================================================================
RCS file: /cvsroot/src/sys/arch/sparc64/dev/tda.c,v
retrieving revision 1.14
diff -u -r1.14 tda.c
--- sys/arch/sparc64/dev/tda.c	31 Oct 2020 13:17:34 -0000	1.14
+++ sys/arch/sparc64/dev/tda.c	7 Dec 2020 15:00:25 -0000
@@ -33,10 +33,24 @@
 
 #include <dev/i2c/i2cvar.h>
 
-/* fan control registers */
-#define TDA_SYSFAN_REG		0xf0
-#define TDA_CPUFAN_REG		0xf2
-#define TDA_PSFAN_REG		0xf4
+#ifdef TDA_DEBUG
+#define DPRINTF printf
+#else
+#define DPRINTF if (0) printf
+#endif
+
+/* fan control registers: instruction bits 4-7, subaddress (port) bits 0-3 */
+#define TDA_REG_AUTOINC		0x00
+#define TDA_REG_NO_INC		0xf0
+
+/* SB1000/2000 */
+#define TDA_B_SYSFAN_REG	0x0
+#define TDA_B_CPUFAN_REG	0x2
+#define TDA_B_PSFAN_REG		0x4
+/* E450 */
+#define TDA_E_CPUFAN_REG	0x0
+#define TDA_E_PSUFAN_REG	0x1
+#define TDA_E_AFBFAN_REG	0x2
 
 #define TDA_FANSPEED_MIN        0x0c
 #define TDA_FANSPEED_MAX        0x3f
@@ -44,41 +58,59 @@
 #define TDA_PSFAN_ON            0x1f
 #define TDA_PSFAN_OFF           0x00
 
-/* Internal and External temperature sensor numbers */
-#define SENSOR_TEMP_EXT		0
-#define SENSOR_TEMP_INT		1
-
+#define TDA_NUM_REG		3	/* There are 8, but we use max 3 */
 /* Fan sensor numbers */
 #define SENSOR_FAN_CPU		0
 #define SENSOR_FAN_SYS		1
+#define SENSOR_FAN_AFB		2
 
 #define DEGC_TO_mK(c)		(((c) * 1000000) + 273150000)
+#define mK_TO_DEGC(k)		((k - 273150000) / 1000000)
 
 #define CPU_TEMP_MAX		DEGC_TO_mK(67)
 #define CPU_TEMP_MIN		DEGC_TO_mK(57)
 #define SYS_TEMP_MAX		DEGC_TO_mK(30)
 #define SYS_TEMP_MIN		DEGC_TO_mK(20)
 
+#define XLATE_SIZE		0x6f	/* Translation tables: 112 entries */
+
+/* E450 temperature difference constants (from OpenSolaris) */
+#define CPU_TEMP_OFFSET		-20
+#define PS_TEMP_OFFSET		-30
+
+struct tda_channel {
+	u_int8_t		chan_fan_type;
+	u_int8_t		chan_fan_speed;
+	u_int8_t		chan_reg_num;
+	u_char			*chan_xlate;
+	int			chan_xlate_off;
+	u_int64_t		chan_temp_max;
+	u_int64_t		chan_temp_min;
+	envsys_data_t		chan_sensor;
+};
+
 struct tda_softc {
 	device_t		sc_dev;
 	i2c_tag_t		sc_tag;
 	i2c_addr_t		sc_addr;
 
-	u_int16_t		sc_cfan_speed;	/* current CPU fan speed */
-	u_int16_t		sc_sfan_speed;	/* current SYS fan speed */
+	u_char			sc_cpu_fan_spd[XLATE_SIZE];
+	u_char			sc_ps_fan_spd[XLATE_SIZE];
+
+	u_int8_t		sc_num_fans;
+	struct tda_channel	sc_channels[TDA_NUM_REG];
 
 	struct sysmon_envsys	*sc_sme;
-	envsys_data_t		sc_sensor[2];
 
 	callout_t		sc_timer;
 };
 
 int	tda_match(device_t, cfdata_t, void *);
 void	tda_attach(device_t, device_t, void *);
-static int	tda_detach(device_t, int);
+int	tda_detach(device_t, int);
 void	tda_refresh(struct sysmon_envsys *, envsys_data_t *);
 
-void	tda_setspeed(struct tda_softc *);
+static int	tda_setspeed(struct tda_softc *);
 static void	tda_adjust(void *);
 static void	tda_timeout(void *);
 
@@ -92,8 +124,9 @@
 {
 	struct i2c_attach_args *ia = aux;
 
-	/* Only attach on the Sun Blade 1000/2000. */
-	if (strcmp(machine_model, "SUNW,Sun-Blade-1000") != 0)
+	/* Only attach on the Sun Blade 1000/2000 and E450. */
+	if (strcmp(machine_model, "SUNW,Sun-Blade-1000") != 0 &&
+	    strcmp(machine_model, "SUNW,Ultra-4") != 0)
 		return (0);
 
 	/*
@@ -112,7 +145,7 @@
 {
 	struct tda_softc *sc = device_private(self);
 	struct i2c_attach_args *ia = aux;
-	int rc;
+	int i, rc, node;
 
 	sc->sc_dev = self;
 	sc->sc_tag = ia->ia_tag;
@@ -121,43 +154,104 @@
 	aprint_normal(": %s\n", ia->ia_name);
 	aprint_naive(": Environment sensor\n");
 
+	/* Set the fan parameters according to the machine model */
+	if (!strcmp(machine_model, "SUNW,Sun-Blade-1000")) {
+		sc->sc_num_fans = 2;
+		sc->sc_channels[0].chan_fan_type = SENSOR_FAN_CPU;
+		sc->sc_channels[0].chan_reg_num = TDA_B_CPUFAN_REG;
+		sc->sc_channels[0].chan_xlate = NULL;
+		sc->sc_channels[0].chan_temp_max = CPU_TEMP_MAX;
+		sc->sc_channels[0].chan_temp_min = CPU_TEMP_MIN;
+		strlcpy(sc->sc_channels[0].chan_sensor.desc,
+		    "fan.cpu",sizeof("fan.cpu"));
+		sc->sc_channels[1].chan_fan_type = SENSOR_FAN_SYS;
+		sc->sc_channels[1].chan_reg_num = TDA_B_SYSFAN_REG;
+		sc->sc_channels[1].chan_xlate = NULL;
+		sc->sc_channels[1].chan_temp_max = SYS_TEMP_MAX;
+		sc->sc_channels[1].chan_temp_min = SYS_TEMP_MIN;
+		strlcpy(sc->sc_channels[1].chan_sensor.desc,
+		    "fan.sys",sizeof("fan.sys"));
+	} else if (!strcmp(machine_model, "SUNW,Ultra-4")) {
+		node = (int)ia->ia_cookie;
+		sc->sc_num_fans = 3;
+		sc->sc_channels[0].chan_fan_type = SENSOR_FAN_CPU;
+		sc->sc_channels[0].chan_reg_num = TDA_E_CPUFAN_REG;
+		if (OF_getprop(node, "cpu-fan-speeds", &sc->sc_cpu_fan_spd,
+		    XLATE_SIZE) > 0) {
+			sc->sc_channels[0].chan_xlate = sc->sc_cpu_fan_spd;
+			sc->sc_channels[0].chan_xlate_off = CPU_TEMP_OFFSET;
+		} else {
+			aprint_error_dev(self,
+			    "couldn't find \"cpu-fan-speeds\" property\n");
+			return;
+		}
+		strlcpy(sc->sc_channels[0].chan_sensor.desc,
+		    "CPUFAN",sizeof("CPUFAN"));
+		sc->sc_channels[1].chan_fan_type = SENSOR_FAN_SYS;
+		sc->sc_channels[1].chan_reg_num = TDA_E_PSUFAN_REG;
+		if (OF_getprop(node, "ps-fan-speeds", &sc->sc_ps_fan_spd,
+		    XLATE_SIZE) > 0) {
+			sc->sc_channels[1].chan_xlate = sc->sc_ps_fan_spd;
+			sc->sc_channels[1].chan_xlate_off = PS_TEMP_OFFSET;
+		} else {
+			aprint_error_dev(self,
+			    "couldn't find \"ps-fan-speeds\" property\n");
+			return;
+		}
+		strlcpy(sc->sc_channels[1].chan_sensor.desc,
+		    "PSUFAN",sizeof("PSUFAN"));
+		sc->sc_channels[2].chan_fan_type = SENSOR_FAN_AFB;
+		sc->sc_channels[2].chan_reg_num = TDA_E_AFBFAN_REG;
+		sc->sc_channels[2].chan_xlate = NULL;
+		/* AFB fan always runs at full speed */
+		sc->sc_channels[2].chan_temp_max = 0;
+		sc->sc_channels[2].chan_temp_min = 0;
+		strlcpy(sc->sc_channels[2].chan_sensor.desc,
+		    "AFBFAN",sizeof("AFBFAN"));
+	} else {
+		aprint_error_dev(self,
+		    "unsupported: %s\n", machine_model);
+		return;
+	}
 	/*
-	 * Set the fans to maximum speed and save the power levels;
+	 * Set the fans to medium speed and save the power levels;
 	 * the controller is write-only.
 	 */
-	sc->sc_cfan_speed = sc->sc_sfan_speed = (TDA_FANSPEED_MAX+TDA_FANSPEED_MIN)/2;
+	for (i = 0; i < sc->sc_num_fans; i++)
+		switch (sc->sc_channels[i].chan_fan_type) {
+		case SENSOR_FAN_CPU:
+			/* Fallthrough */
+		case SENSOR_FAN_SYS:
+			sc->sc_channels[i].chan_fan_speed =
+			    (TDA_FANSPEED_MAX+TDA_FANSPEED_MIN)/2;
+			break;
+		default:
+			sc->sc_channels[i].chan_fan_speed = TDA_FANSPEED_MAX;
+			break;
+		}
 	tda_setspeed(sc);
-	
+
 	callout_init(&sc->sc_timer, CALLOUT_MPSAFE);
 	callout_reset(&sc->sc_timer, hz*20, tda_timeout, sc);
 
 	/* Initialise sensor data */
-	sc->sc_sensor[SENSOR_FAN_CPU].state = ENVSYS_SINVALID;
-	sc->sc_sensor[SENSOR_FAN_CPU].units = ENVSYS_INTEGER;
-	sc->sc_sensor[SENSOR_FAN_CPU].flags = ENVSYS_FMONNOTSUPP;
-	strlcpy(sc->sc_sensor[SENSOR_FAN_CPU].desc,
-	    "fan.cpu",sizeof("fan.cpu"));
-	sc->sc_sensor[SENSOR_FAN_SYS].state = ENVSYS_SINVALID;
-	sc->sc_sensor[SENSOR_FAN_SYS].units = ENVSYS_INTEGER;
-	sc->sc_sensor[SENSOR_FAN_SYS].flags = ENVSYS_FMONNOTSUPP;
-	strlcpy(sc->sc_sensor[SENSOR_FAN_SYS].desc,
-	    "fan.sys",sizeof("fan.sys"));
-	sc->sc_sme = sysmon_envsys_create();
-	rc = sysmon_envsys_sensor_attach(
-	    sc->sc_sme, &sc->sc_sensor[SENSOR_FAN_CPU]);
-	if (rc) {
-		sysmon_envsys_destroy(sc->sc_sme);
-		aprint_error_dev(self,
-		    "unable to attach cpu fan at sysmon, error %d\n", rc);
-		return;
+	for (i = 0; i < sc->sc_num_fans; i++) {
+		sc->sc_channels[i].chan_sensor.state = ENVSYS_SINVALID;
+		sc->sc_channels[i].chan_sensor.units = ENVSYS_INTEGER;
+		sc->sc_channels[i].chan_sensor.flags = ENVSYS_FMONNOTSUPP;
 	}
-	rc = sysmon_envsys_sensor_attach(
-	    sc->sc_sme, &sc->sc_sensor[SENSOR_FAN_SYS]);
-	if (rc) {
-		sysmon_envsys_destroy(sc->sc_sme);
-		aprint_error_dev(self,
-		    "unable to attach sys fan at sysmon, error %d\n", rc);
-		return;
+	sc->sc_sme = sysmon_envsys_create();
+	for (i = 0; i < sc->sc_num_fans; i++) {
+		rc = sysmon_envsys_sensor_attach(
+		    sc->sc_sme, &sc->sc_channels[i].chan_sensor);
+		if (rc) {
+			sysmon_envsys_destroy(sc->sc_sme);
+			sc->sc_sme = NULL;
+			aprint_error_dev(self,
+			    "unable to attach %s fan at sysmon, error %d\n",
+			    sc->sc_channels[i].chan_sensor.desc, rc);
+			return;
+		}
 	}
         sc->sc_sme->sme_name = device_xname(self);
         sc->sc_sme->sme_cookie = sc;
@@ -167,6 +261,7 @@
 		aprint_error_dev(self,
 		    "unable to register with sysmon, error %d\n", rc);
 		sysmon_envsys_destroy(sc->sc_sme);
+		sc->sc_sme = NULL;
 		return;
 	}
 }
@@ -175,15 +270,26 @@
 tda_detach(device_t self, int flags)
 {
 	struct tda_softc *sc = device_private(self);
+	int i;
 
-	if (sc->sc_sme != NULL)
+	if (sc->sc_sme != NULL) {
 		sysmon_envsys_unregister(sc->sc_sme);
+		sc->sc_sme = NULL;
+	}
 
 	callout_halt(&sc->sc_timer, NULL);
 	callout_destroy(&sc->sc_timer);
 
-	sc->sc_cfan_speed = sc->sc_sfan_speed = TDA_FANSPEED_MAX;
-	tda_setspeed(sc);
+	for (i = 0; i < sc->sc_num_fans; i++)
+		sc->sc_channels[i].chan_fan_speed = TDA_FANSPEED_MAX;
+
+	for (i = 0; i < 5; i++) {	/* Loop in case the bus is busy */
+		if (!tda_setspeed(sc))
+			return 0;
+		delay(10000);
+	}
+
+	aprint_error_dev(sc->sc_dev, "cannot set fan speeds\n");
 	return 0;
 }
 
@@ -196,108 +302,162 @@
 	callout_reset(&sc->sc_timer, hz*60, tda_timeout, sc);
 }
 
-void
+static int
 tda_setspeed(struct tda_softc *sc)
 {
 	u_int8_t cmd[2];
+	int i, rc;
 
-	if (sc->sc_cfan_speed < TDA_FANSPEED_MIN)
-		sc->sc_cfan_speed = TDA_FANSPEED_MIN;
-	if (sc->sc_sfan_speed < TDA_FANSPEED_MIN)
-		sc->sc_sfan_speed = TDA_FANSPEED_MIN;
-	if (sc->sc_cfan_speed > TDA_FANSPEED_MAX)
-		sc->sc_cfan_speed = TDA_FANSPEED_MAX;
-	if (sc->sc_sfan_speed > TDA_FANSPEED_MAX)
-		sc->sc_sfan_speed = TDA_FANSPEED_MAX;
-
-	iic_acquire_bus(sc->sc_tag, 0);
-
-	cmd[0] = TDA_CPUFAN_REG;
-	cmd[1] = sc->sc_cfan_speed;
-	if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
-	    sc->sc_addr, &cmd, sizeof(cmd), NULL, 0, 0)) {
-		aprint_error_dev(sc->sc_dev, "cannot write cpu-fan register\n");
+	for (i = 0; i < sc->sc_num_fans; i++) {
+		if (sc->sc_channels[i].chan_fan_speed < TDA_FANSPEED_MIN)
+			sc->sc_channels[i].chan_fan_speed = TDA_FANSPEED_MIN;
+		if (sc->sc_channels[i].chan_fan_speed > TDA_FANSPEED_MAX)
+			sc->sc_channels[i].chan_fan_speed = TDA_FANSPEED_MAX;
+
+		rc = iic_acquire_bus(sc->sc_tag, 0);
+		if (rc) {
+			aprint_error_dev(sc->sc_dev,
+			    "cannot acquire i2c bus for %s\n",
+			    sc->sc_channels[i].chan_sensor.desc);
+			return rc;
+		}
+		cmd[0] = sc->sc_channels[i].chan_reg_num | TDA_REG_NO_INC;
+		cmd[1] = sc->sc_channels[i].chan_fan_speed;
+		rc = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
+		    sc->sc_addr, &cmd, sizeof(cmd), NULL, 0, 0);
+		if (rc) {
+			aprint_error_dev(sc->sc_dev,
+			    "cannot write register for %s\n",
+			    sc->sc_channels[i].chan_sensor.desc);
+			iic_release_bus(sc->sc_tag, 0);
+			return rc;
+		}
 		iic_release_bus(sc->sc_tag, 0);
-		return;
-        }
 
-	cmd[0] = TDA_SYSFAN_REG;
-	cmd[1] = sc->sc_sfan_speed;
-	if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
-	    sc->sc_addr, &cmd, sizeof(cmd), NULL, 0, 0)) {
-		aprint_error_dev(sc->sc_dev, "cannot write system-fan register\n");
-		iic_release_bus(sc->sc_tag, 0);
-		return;
+		DPRINTF("%s: changed %s fan speed to %d\n",
+		    device_xname(sc->sc_dev),
+		    sc->sc_channels[i].chan_sensor.desc,
+		    sc->sc_channels[i].chan_fan_speed);
         }
-
-	iic_release_bus(sc->sc_tag, 0);
-
-	aprint_debug_dev(sc->sc_dev, "changed fan speed to cpu=%d system=%d\n",
-		sc->sc_cfan_speed, sc->sc_sfan_speed);
+	return 0;
 }
 
 static bool
 is_cpu_sensor(const envsys_data_t *edata)
 {
+	int i;
+	char name[8];
+
 	if (edata->units != ENVSYS_STEMP)
 		return false;
-	return strcmp(edata->desc, "external") == 0;
+	if (!strcmp(machine_model, "SUNW,Sun-Blade-1000"))
+		return strcmp(edata->desc, "external") == 0;
+	if (!strcmp(machine_model, "SUNW,Ultra-4"))
+		for (i = 0; i < 4; i++) {
+			snprintf(name, 8, "CPU%d", i);
+			if (strcmp(edata->desc, name) == 0)
+				return true;
+		}
+	return false;
 }
 
 static bool
 is_system_sensor(const envsys_data_t *edata)
 {
+	int i;
+	char name[8];
+
 	if (edata->units != ENVSYS_STEMP)
 		return false;
-	return strcmp(edata->desc, "internal") == 0;
+	if (!strcmp(machine_model, "SUNW,Sun-Blade-1000"))
+		return strcmp(edata->desc, "internal") == 0;
+	if (!strcmp(machine_model, "SUNW,Ultra-4"))
+		for (i = 0; i < 4; i++) {
+			snprintf(name, 8, "PS%d", i);
+			if (strcmp(edata->desc, name) == 0)
+				return true;
+		}
+	return false;
 }
 
 static void
 tda_adjust(void *v)
 {
 	struct tda_softc *sc = v;
-	u_int64_t ctemp, stemp;
-	u_int16_t cspeed, sspeed;
-
-	/* Default to running the fans at maximum speed. */
-	sspeed = cspeed = TDA_FANSPEED_MAX;
-
-	/* fetch maximum current temperature */
-	ctemp = sysmon_envsys_get_max_value(is_cpu_sensor, true);
-	stemp = sysmon_envsys_get_max_value(is_system_sensor, true);
-
-	/* the predicates for selecting sensors must have gone wrong */
-	if (ctemp == 0 || stemp == 0) {
-		aprint_error_dev(sc->sc_dev, "skipping temp adjustment"
-			" - no sensor values\n");
-		return;
+	struct tda_channel *chp;
+	u_int64_t temp, temp_max, temp_min;
+	u_int8_t speed;
+	int i, idx, changed = 0;
+
+	for (i = 0; i < sc->sc_num_fans; i++) {
+		chp = &sc->sc_channels[i];
+		temp = 0;
+
+		/* Default to running the fans at maximum speed. */
+		speed = TDA_FANSPEED_MAX;
+
+		/* fetch maximum current temperature */
+		switch (chp->chan_fan_type) {
+		case SENSOR_FAN_CPU:
+			temp = sysmon_envsys_get_max_value(is_cpu_sensor,
+			    true);
+			break;
+		case SENSOR_FAN_SYS:
+			temp = sysmon_envsys_get_max_value(is_system_sensor,
+			    true);
+			break;
+		default:
+			temp = 1;	/* Any temperature > 0 */
+			break;
+		}
+
+		/* the predicates for selecting sensors must have gone wrong */
+		if (temp == 0) {
+			aprint_error_dev(sc->sc_dev, "skipping temp adjustment"
+			    " - no sensor values for %s\n",
+			    chp->chan_sensor.desc);
+			return;
+		}
+
+		DPRINTF("%s: current temperature for %s: %" PRIu64 " mK\n",
+		    device_xname(sc->sc_dev), chp->chan_sensor.desc, temp);
+
+		if (chp->chan_xlate == NULL) {
+			temp_max = chp->chan_temp_max;
+			temp_min = chp->chan_temp_min;
+			DPRINTF("%s: temperature range: %" PRIu64
+			    " - %" PRIu64 "\n", device_xname(sc->sc_dev),
+			    temp_min, temp_max);
+			if (temp < temp_min)
+				speed = TDA_FANSPEED_MIN;
+			else if (temp < temp_max)
+				speed = TDA_FANSPEED_MIN + (temp - temp_min) *
+				    (TDA_FANSPEED_MAX - TDA_FANSPEED_MIN) /
+				    (temp_max - temp_min);
+		} else {
+			/* Translation tables are in Celsius */
+			idx = mK_TO_DEGC(temp);
+			idx += chp->chan_xlate_off;
+			if (idx >= XLATE_SIZE)
+				idx = XLATE_SIZE - 1;
+			if (idx < 0)
+				idx = 0;
+			speed = chp->chan_xlate[idx];
+			DPRINTF("%s: temperature index (speed): %d (%d)\n",
+			    device_xname(sc->sc_dev), idx, speed);
+		}
+		if (speed != chp->chan_fan_speed) {
+			changed = 1;
+			DPRINTF("%s: fan speed change: %d > %d\n",
+			    device_xname(sc->sc_dev),
+			    chp->chan_fan_speed, speed);
+			chp->chan_fan_speed = speed;
+		}
 	}
 
-	aprint_debug_dev(sc->sc_dev, "current temperature: cpu %" PRIu64
-		" system %" PRIu64 "\n",
-		ctemp, stemp);
-
-	if (ctemp < CPU_TEMP_MIN)
-		cspeed = TDA_FANSPEED_MIN;
-	else if (ctemp < CPU_TEMP_MAX)
-		cspeed = TDA_FANSPEED_MIN +
-			(ctemp - CPU_TEMP_MIN) * 
-			(TDA_FANSPEED_MAX - TDA_FANSPEED_MIN) / 
-			(CPU_TEMP_MAX - CPU_TEMP_MIN);
-
-	if (stemp < SYS_TEMP_MIN)
-		sspeed = TDA_FANSPEED_MIN;
-	else if (stemp < SYS_TEMP_MAX)
-		sspeed = TDA_FANSPEED_MIN +
-			(stemp - SYS_TEMP_MIN) * 
-			(TDA_FANSPEED_MAX - TDA_FANSPEED_MIN) / 
-			(SYS_TEMP_MAX - SYS_TEMP_MIN);
-
-	if (sspeed == sc->sc_sfan_speed && cspeed == sc->sc_cfan_speed)
+	if (!changed)
 		return;
 
-	sc->sc_sfan_speed = sspeed;
-	sc->sc_cfan_speed = cspeed;
 	tda_setspeed(sc);
 }
 
@@ -305,12 +465,9 @@
 tda_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
 {
 	struct tda_softc *sc = sme->sme_cookie;
-	u_int16_t speed;
+	u_int8_t speed;
 
-	if (edata->sensor == SENSOR_FAN_CPU)
-		speed = sc->sc_cfan_speed;
-	else
-		speed = sc->sc_sfan_speed;
+	speed = sc->sc_channels[edata->sensor].chan_fan_speed;
 	if (!speed)
 		edata->state = ENVSYS_SINVALID;
 	else {
@@ -318,4 +475,3 @@
 		edata->state = ENVSYS_SVALID;
 	}
 }
-
Index: sys/arch/sparc64/sparc64/autoconf.c
===================================================================
RCS file: /cvsroot/src/sys/arch/sparc64/sparc64/autoconf.c,v
retrieving revision 1.227
diff -u -r1.227 autoconf.c
--- sys/arch/sparc64/sparc64/autoconf.c	29 Oct 2020 06:47:38 -0000	1.227
+++ sys/arch/sparc64/sparc64/autoconf.c	7 Dec 2020 15:00:25 -0000
@@ -1090,10 +1090,11 @@
 			}
 		} 
 		if (device_is_a(dev, "pcf8574io")) {
-			if (!strcmp(machine_model, "SUNW,Ultra-250")) {
+			if (!strcmp(machine_model, "SUNW,Ultra-250"))
 				add_gpio_props_e250(dev, aux);
-			}
-		} 
+			if (!strcmp(machine_model, "SUNW,Ultra-4"))
+				add_gpio_props_e450(dev, aux);
+		}
 	} else if (device_is_a(dev, "sd") || device_is_a(dev, "cd")) {
 		struct scsipibus_attach_args *sa = aux;
 		struct scsipi_periph *periph = sa->sa_periph;
Index: sys/arch/sparc64/sparc64/ofw_patch.c
===================================================================
RCS file: /cvsroot/src/sys/arch/sparc64/sparc64/ofw_patch.c,v
retrieving revision 1.6
diff -u -r1.6 ofw_patch.c
--- sys/arch/sparc64/sparc64/ofw_patch.c	29 Oct 2020 06:47:38 -0000	1.6
+++ sys/arch/sparc64/sparc64/ofw_patch.c	7 Dec 2020 15:00:25 -0000
@@ -134,6 +134,8 @@
 	prop_array_t pins;
 
 	switch (ia->ia_addr) {
+		case 0x38:	/* interrupt status */
+			break;
 		case 0x39:	/* PSU status */
 			pins = prop_array_create();
 			add_gpio_pin(pins, "INDICATOR psu0_present", 0, 0, -1);
@@ -160,7 +162,7 @@
 			prop_dictionary_set(dict, "pins", pins);
 			prop_object_release(pins);
 			break;
-		case 0x3e:	/* front panel LEDs */
+		case 0x3e:	/* front panel LEDs (E250/E450) */
 			pins = prop_array_create();
 			add_gpio_pin(pins, "LED disk_fault", 0, 0, -1);
 			add_gpio_pin(pins, "LED psu_fault", 1, 0, -1);
@@ -189,6 +191,110 @@
 }
 
 void
+add_gpio_props_e450(device_t dev, void *aux)
+{
+	struct i2c_attach_args *ia = aux;
+	prop_dictionary_t dict = device_properties(dev);
+	prop_array_t pins;
+	char name[20];
+	int i;
+
+	switch (ia->ia_addr) {
+		case 0x20:	/* disk shelf 0 */
+			pins = prop_array_create();
+			for (i = 0; i < 4; i++) {
+				snprintf(name, sizeof(name),
+					"LED disk%d_fault", i);
+				add_gpio_pin(pins, name, i, 0, -1);
+			}
+			prop_dictionary_set(dict, "pins", pins);
+			prop_object_release(pins);
+			break;
+		case 0x21:	/* disk shelf 1 */
+			pins = prop_array_create();
+			for (i = 0; i < 8; i++) {
+				snprintf(name, sizeof(name),
+					"LED disk%d_fault", i + 4);
+				add_gpio_pin(pins, name, i, 0, -1);
+			}
+			prop_dictionary_set(dict, "pins", pins);
+			prop_object_release(pins);
+			break;
+		case 0x22:	/* disk shelf 2 */
+			pins = prop_array_create();
+			for (i = 0; i < 8; i++) {
+				snprintf(name, sizeof(name),
+					"LED disk%d_fault", i + 12);
+				add_gpio_pin(pins, name, i, 0, -1);
+			}
+			prop_dictionary_set(dict, "pins", pins);
+			prop_object_release(pins);
+			break;
+		case 0x38:	/* interrupt status */
+			break;
+		case 0x39:	/* PSU 2 status */
+			pins = prop_array_create();
+			add_gpio_pin(pins, "INDICATOR psu2_present", 0, 0, -1);
+			add_gpio_pin(pins, "INDICATOR psu2_550W", 1, 0, -1);
+			add_gpio_pin(pins, "INDICATOR psu2_650W", 2, 0, -1);
+			add_gpio_pin(pins, "INDICATOR psu2_DC_fault", 3, 0, -1);
+			add_gpio_pin(pins, "INDICATOR psu2_overload", 4, 0, -1);
+			add_gpio_pin(pins, "INDICATOR psu2_load_flt", 5, 0, -1);
+			prop_dictionary_set(dict, "pins", pins);
+			prop_object_release(pins);
+			break;
+		case 0x3a:	/* PSU 1 status */
+			pins = prop_array_create();
+			add_gpio_pin(pins, "INDICATOR psu1_present", 0, 0, -1);
+			add_gpio_pin(pins, "INDICATOR psu1_550W", 1, 0, -1);
+			add_gpio_pin(pins, "INDICATOR psu1_650W", 2, 0, -1);
+			add_gpio_pin(pins, "INDICATOR psu1_DC_fault", 3, 0, -1);
+			add_gpio_pin(pins, "INDICATOR psu1_overload", 4, 0, -1);
+			add_gpio_pin(pins, "INDICATOR psu1_load_flt", 5, 0, -1);
+			prop_dictionary_set(dict, "pins", pins);
+			prop_object_release(pins);
+			break;
+		case 0x3b:	/* PSU 0 status */
+			pins = prop_array_create();
+			add_gpio_pin(pins, "INDICATOR psu0_present", 0, 0, -1);
+			add_gpio_pin(pins, "INDICATOR psu0_550W", 1, 0, -1);
+			add_gpio_pin(pins, "INDICATOR psu0_650W", 2, 0, -1);
+			add_gpio_pin(pins, "INDICATOR psu0_DC_fault", 3, 0, -1);
+			add_gpio_pin(pins, "INDICATOR psu0_overload", 4, 0, -1);
+			add_gpio_pin(pins, "INDICATOR psu0_load_flt", 5, 0, -1);
+			prop_dictionary_set(dict, "pins", pins);
+			prop_object_release(pins);
+			break;
+		case 0x3c:	/* fan failure status */
+			pins = prop_array_create();
+			add_gpio_pin(pins, "INDICATOR psu2_fanfail", 0, 0, -1);
+			add_gpio_pin(pins, "INDICATOR psu1_fanfail", 1, 0, -1);
+			add_gpio_pin(pins, "INDICATOR psu0_fanfail", 2, 0, -1);
+			add_gpio_pin(pins, "INDICATOR cpu0_fanfail", 3, 0, -1);
+			add_gpio_pin(pins, "INDICATOR cpu1_fanfail", 4, 0, -1);
+			add_gpio_pin(pins, "INDICATOR cpu2_fanfail", 5, 0, -1);
+			add_gpio_pin(pins, "INDICATOR afb_fanfail", 6, 0, -1);
+			prop_dictionary_set(dict, "pins", pins);
+			prop_object_release(pins);
+			break;
+		case 0x3e:	/* front panel LEDs (E250/E450) */
+			pins = prop_array_create();
+			add_gpio_pin(pins, "LED disk_fault", 0, 0, -1);
+			add_gpio_pin(pins, "LED psu_fault", 1, 0, -1);
+			add_gpio_pin(pins, "LED overtemp", 2, 0, -1);
+			add_gpio_pin(pins, "LED fault", 3, 0, -1);
+			add_gpio_pin(pins, "LED activity", 4, 0, -1);
+			/* Pin 5 is power LED, but not controllable */
+			add_gpio_pin(pins, "INDICATOR key_normal", 6, 0, -1);
+			add_gpio_pin(pins, "INDICATOR key_diag", 7, 0, -1);
+			/* If not "normal" or "diag", key is "lock" */
+			prop_dictionary_set(dict, "pins", pins);
+			prop_object_release(pins);
+			break;
+	}
+}
+
+void
 add_drivebay_props(device_t dev, int ofnode, void *aux)
 {
 	struct scsipibus_attach_args *sa = aux;
@@ -275,10 +381,23 @@
 add_i2c_props_e450(device_t busdev, uint64_t node)
 {
 	prop_array_t cfg;
+	int i;
 
 	DPRINTF(ACDB_PROBE, ("\nAdding sensors for %s ", machine_model));
 	cfg = create_i2c_dict(busdev);
 
+	/* Disk LED GPIO's */
+	for (i = 0x20; i <= 0x22; i++)
+		add_i2c_device(cfg, "gpio", "i2c-pcf8574", i, node);
+
+	/* TDA8444 fan speed controller */
+	add_i2c_device(cfg, "fan-control", "i2c-tda8444", 0x27, node);
+
+	/* Other GPIO's */
+	for (i = 0x38; i <= 0x3c; i++)
+		add_i2c_device(cfg, "gpio", "i2c-pcf8574", i, node);
+	add_i2c_device(cfg, "gpio", "i2c-pcf8574", 0x3e, node);
+
 	/* Power supply 1 temperature. */
 	add_i2c_device(cfg, "PSU-1", "ecadc", 0x48, node);
 
@@ -294,6 +413,9 @@
 	/* CPU temperatures. */
 	add_i2c_device(cfg, "CPU", "ecadc", 0x4f, node);
 
+	/* Watchdog. */
+	add_i2c_device(cfg, "watchdog", "i2c-pcf8583", 0x50, node);
+
 	prop_object_release(cfg);
 }
 
@@ -306,18 +428,18 @@
 	DPRINTF(ACDB_PROBE, ("\nAdding sensors for %s ", machine_model));
 	cfg = create_i2c_dict(busdev);
 
-	/* PSU temperature / CPU fan */
-	add_i2c_device(cfg, "PSU", "ecadc", 0x4a, node);
-
-	/* CPU & system board temperature */
-	add_i2c_device(cfg, "CPU", "ecadc", 0x4f, node);
-
 	/* GPIO's */
 	for (i = 0x38; i <= 0x39; i++)
 		add_i2c_device(cfg, "gpio", "i2c-pcf8574", i, node);
 	for (i = 0x3d; i <= 0x3f; i++)
 		add_i2c_device(cfg, "gpio", "i2c-pcf8574", i, node);
 
+	/* PSU temperature / CPU fan */
+	add_i2c_device(cfg, "PSU", "ecadc", 0x4a, node);
+
+	/* CPU & system board temperature */
+	add_i2c_device(cfg, "CPU", "ecadc", 0x4f, node);
+
 	/* NVRAM */
 	add_i2c_device(cfg, "nvram", "i2c-at24c02", 0x52, node);
 
Index: sys/arch/sparc64/sparc64/ofw_patch.h
===================================================================
RCS file: /cvsroot/src/sys/arch/sparc64/sparc64/ofw_patch.h,v
retrieving revision 1.4
diff -u -r1.4 ofw_patch.h
--- sys/arch/sparc64/sparc64/ofw_patch.h	29 Oct 2020 06:47:38 -0000	1.4
+++ sys/arch/sparc64/sparc64/ofw_patch.h	7 Dec 2020 15:00:25 -0000
@@ -43,6 +43,7 @@
 
 void add_gpio_props_v210(device_t, void *);
 void add_gpio_props_e250(device_t, void *);
+void add_gpio_props_e450(device_t, void *);
 void add_drivebay_props(device_t, int, void *);
 void add_spdmem_props_sparcle(device_t);
 void add_env_sensors_v210(device_t);