Index: src/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 --- src/sys/arch/sparc64/dev/tda.c 31 Oct 2020 13:17:34 -0000 1.14 +++ src/sys/arch/sparc64/dev/tda.c 7 Sep 2026 08:04:36 -0000 @@ -33,10 +33,24 @@ #include -/* 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 */ +#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, node, rc; 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 = devhandle_to_of(device_handle(self)); + 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; } } -