mirror of
https://github.com/freebsd/freebsd-src
synced 2024-10-17 13:53:39 +00:00
Update the I2C-based temperature/fan drivers to connect to the Powermac
thermal control module. This provides automatic fan management on all G5 PowerMacs and Xserves.
This commit is contained in:
parent
3204c8e596
commit
815d7d92c1
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=222458
|
@ -49,6 +49,7 @@ __FBSDID("$FreeBSD$");
|
|||
|
||||
#include <dev/ofw/openfirm.h>
|
||||
#include <dev/ofw/ofw_bus.h>
|
||||
#include <powerpc/powermac/powermac_thermal.h>
|
||||
|
||||
#define FCU_ZERO_C_TO_K 2732
|
||||
|
||||
|
@ -63,8 +64,9 @@ __FBSDID("$FreeBSD$");
|
|||
uint8_t adc741x_config;
|
||||
|
||||
struct ad7417_sensor {
|
||||
struct pmac_therm therm;
|
||||
device_t dev;
|
||||
int id;
|
||||
char location[32];
|
||||
enum {
|
||||
ADC7417_TEMP_SENSOR,
|
||||
ADC7417_ADC_SENSOR
|
||||
|
@ -83,6 +85,9 @@ static int ad7417_read_1(device_t dev, uint32_t addr, uint8_t reg,
|
|||
uint8_t *data);
|
||||
static int ad7417_read_2(device_t dev, uint32_t addr, uint8_t reg,
|
||||
uint16_t *data);
|
||||
static int ad7417_diode_read(struct ad7417_sensor *sens);
|
||||
static int ad7417_adc_read(struct ad7417_sensor *sens);
|
||||
static int ad7417_sensor_read(struct ad7417_sensor *sens);
|
||||
|
||||
struct ad7417_softc {
|
||||
device_t sc_dev;
|
||||
|
@ -243,7 +248,7 @@ ad7417_fill_sensor_prop(device_t dev)
|
|||
sizeof(location));
|
||||
while (len < prop_len) {
|
||||
if (sc->sc_sensors != NULL)
|
||||
strcpy(sc->sc_sensors[i].location, location + len);
|
||||
strcpy(sc->sc_sensors[i].therm.name, location + len);
|
||||
prev_len = strlen(location + len) + 1;
|
||||
len += prev_len;
|
||||
i++;
|
||||
|
@ -251,7 +256,7 @@ ad7417_fill_sensor_prop(device_t dev)
|
|||
if (sc->sc_sensors == NULL)
|
||||
return (i);
|
||||
|
||||
/* Fill the fan type property. */
|
||||
/* Fill the sensor type property. */
|
||||
len = 0;
|
||||
i = 0;
|
||||
prev_len = 0;
|
||||
|
@ -271,6 +276,36 @@ ad7417_fill_sensor_prop(device_t dev)
|
|||
for (j = 0; j < i; j++)
|
||||
sc->sc_sensors[j].id = id[j];
|
||||
|
||||
/* Fill the sensor zone property. Taken from OF. */
|
||||
prop_len = OF_getprop(child, "hwsensor-zone", id, sizeof(id));
|
||||
for (j = 0; j < i; j++)
|
||||
sc->sc_sensors[j].therm.zone = id[j];
|
||||
|
||||
/* Finish setting up sensor properties */
|
||||
for (j = 0; j < i; j++) {
|
||||
sc->sc_sensors[j].dev = dev;
|
||||
|
||||
/* HACK: Apple wired a random diode to the ADC line */
|
||||
if (strstr(sc->sc_sensors[j].therm.name, "DIODE TEMP")
|
||||
!= NULL) {
|
||||
sc->sc_sensors[j].type = ADC7417_TEMP_SENSOR;
|
||||
sc->sc_sensors[j].therm.read =
|
||||
(int (*)(struct pmac_therm *))(ad7417_diode_read);
|
||||
} else {
|
||||
sc->sc_sensors[j].therm.read =
|
||||
(int (*)(struct pmac_therm *))(ad7417_sensor_read);
|
||||
}
|
||||
|
||||
if (sc->sc_sensors[j].type != ADC7417_TEMP_SENSOR)
|
||||
continue;
|
||||
|
||||
/* Make up some ranges */
|
||||
sc->sc_sensors[j].therm.target_temp = 500 + 2732;
|
||||
sc->sc_sensors[j].therm.max_temp = 900 + 2732;
|
||||
|
||||
pmac_thermal_sensor_register(&sc->sc_sensors[j].therm);
|
||||
}
|
||||
|
||||
return (i);
|
||||
}
|
||||
|
||||
|
@ -310,8 +345,9 @@ ad7417_attach(device_t dev)
|
|||
|
||||
/* Add sysctls for the sensors. */
|
||||
for (i = 0; i < sc->sc_nsensors; i++) {
|
||||
for (j = 0; j < strlen(sc->sc_sensors[i].location); j++) {
|
||||
sysctl_name[j] = tolower(sc->sc_sensors[i].location[j]);
|
||||
for (j = 0; j < strlen(sc->sc_sensors[i].therm.name); j++) {
|
||||
sysctl_name[j] =
|
||||
tolower(sc->sc_sensors[i].therm.name[j]);
|
||||
if (isspace(sysctl_name[j]))
|
||||
sysctl_name[j] = '_';
|
||||
}
|
||||
|
@ -341,7 +377,7 @@ ad7417_attach(device_t dev)
|
|||
device_printf(dev, "Sensors\n");
|
||||
for (i = 0; i < sc->sc_nsensors; i++) {
|
||||
device_printf(dev, "Location: %s ID: %d type: %d\n",
|
||||
sc->sc_sensors[i].location,
|
||||
sc->sc_sensors[i].therm.name,
|
||||
sc->sc_sensors[i].id,
|
||||
sc->sc_sensors[i].type);
|
||||
}
|
||||
|
@ -391,44 +427,91 @@ ad7417_get_adc(device_t dev, uint32_t addr, unsigned int *value,
|
|||
}
|
||||
|
||||
static int
|
||||
ad7417_sensor_read(device_t dev, struct ad7417_sensor *sens, int *temp)
|
||||
ad7417_diode_read(struct ad7417_sensor *sens)
|
||||
{
|
||||
static int eeprom_read = 0;
|
||||
static cell_t eeprom[2][40];
|
||||
phandle_t eeprom_node;
|
||||
int rawval, diode_slope, diode_offset;
|
||||
int temp;
|
||||
|
||||
if (!eeprom_read) {
|
||||
eeprom_node = OF_finddevice("/u3/i2c/cpuid@a0");
|
||||
OF_getprop(eeprom_node, "cpuid", eeprom[0], sizeof(eeprom[0]));
|
||||
eeprom_node = OF_finddevice("/u3/i2c/cpuid@a2");
|
||||
OF_getprop(eeprom_node, "cpuid", eeprom[1], sizeof(eeprom[1]));
|
||||
eeprom_read = 1;
|
||||
}
|
||||
|
||||
rawval = ad7417_adc_read(sens);
|
||||
if (strstr(sens->therm.name, "CPU B") != NULL) {
|
||||
diode_slope = eeprom[1][0x11] >> 16;
|
||||
diode_offset = (int16_t)(eeprom[1][0x11] & 0xffff) << 12;
|
||||
} else {
|
||||
diode_slope = eeprom[0][0x11] >> 16;
|
||||
diode_offset = (int16_t)(eeprom[0][0x11] & 0xffff) << 12;
|
||||
}
|
||||
|
||||
temp = (rawval*diode_slope + diode_offset) >> 2;
|
||||
temp = (10*(temp >> 16)) + ((10*(temp & 0xffff)) >> 16);
|
||||
|
||||
return (temp + FCU_ZERO_C_TO_K);
|
||||
}
|
||||
|
||||
static int
|
||||
ad7417_adc_read(struct ad7417_sensor *sens)
|
||||
{
|
||||
struct ad7417_softc *sc;
|
||||
uint8_t chan;
|
||||
int temp;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
sc = device_get_softc(sens->dev);
|
||||
|
||||
switch (sens->id) {
|
||||
case 11:
|
||||
case 16:
|
||||
chan = 1;
|
||||
break;
|
||||
case 12:
|
||||
case 17:
|
||||
chan = 2;
|
||||
break;
|
||||
case 13:
|
||||
case 18:
|
||||
chan = 3;
|
||||
break;
|
||||
case 14:
|
||||
case 19:
|
||||
chan = 4;
|
||||
break;
|
||||
default:
|
||||
chan = 1;
|
||||
}
|
||||
|
||||
ad7417_get_adc(sc->sc_dev, sc->sc_addr, &temp, chan);
|
||||
|
||||
return (temp);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
ad7417_sensor_read(struct ad7417_sensor *sens)
|
||||
{
|
||||
struct ad7417_softc *sc;
|
||||
int temp;
|
||||
|
||||
sc = device_get_softc(sens->dev);
|
||||
|
||||
/* Init the ADC. */
|
||||
ad7417_init_adc(sc->sc_dev, sc->sc_addr);
|
||||
|
||||
if (sens->type == ADC7417_TEMP_SENSOR) {
|
||||
ad7417_get_temp(sc->sc_dev, sc->sc_addr, temp);
|
||||
*temp += FCU_ZERO_C_TO_K;
|
||||
ad7417_get_temp(sc->sc_dev, sc->sc_addr, &temp);
|
||||
temp += FCU_ZERO_C_TO_K;
|
||||
} else {
|
||||
uint8_t chan;
|
||||
switch (sens->id) {
|
||||
case 11:
|
||||
case 16:
|
||||
chan = 1;
|
||||
break;
|
||||
case 12:
|
||||
case 17:
|
||||
chan = 2;
|
||||
break;
|
||||
case 13:
|
||||
case 18:
|
||||
chan = 3;
|
||||
break;
|
||||
case 14:
|
||||
case 19:
|
||||
chan = 4;
|
||||
break;
|
||||
default:
|
||||
chan = 1;
|
||||
}
|
||||
|
||||
ad7417_get_adc(sc->sc_dev, sc->sc_addr, temp, chan);
|
||||
temp = ad7417_adc_read(sens);
|
||||
}
|
||||
return (0);
|
||||
return (temp);
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -439,19 +522,16 @@ ad7417_sensor_sysctl(SYSCTL_HANDLER_ARGS)
|
|||
struct ad7417_sensor *sens;
|
||||
int value = 0;
|
||||
int error;
|
||||
int temp;
|
||||
|
||||
dev = arg1;
|
||||
sc = device_get_softc(dev);
|
||||
sens = &sc->sc_sensors[arg2];
|
||||
|
||||
error = ad7417_sensor_read(dev, sens, &value);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
value = sens->therm.read(&sens->therm);
|
||||
if (value < 0)
|
||||
return (ENXIO);
|
||||
|
||||
temp = value;
|
||||
|
||||
error = sysctl_handle_int(oidp, &temp, 0, req);
|
||||
error = sysctl_handle_int(oidp, &value, 0, req);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
|
|
@ -49,33 +49,31 @@ __FBSDID("$FreeBSD$");
|
|||
|
||||
#include <dev/ofw/openfirm.h>
|
||||
#include <dev/ofw/ofw_bus.h>
|
||||
#include <powerpc/powermac/powermac_thermal.h>
|
||||
|
||||
#define FCU_ZERO_C_TO_K 2732
|
||||
|
||||
/* Drivebay sensor: LM75/DS1775. */
|
||||
#define DS1775_TEMP 0x0
|
||||
|
||||
struct ds1775_sensor {
|
||||
char location[32];
|
||||
};
|
||||
|
||||
/* Regular bus attachment functions */
|
||||
static int ds1775_probe(device_t);
|
||||
static int ds1775_attach(device_t);
|
||||
|
||||
struct ds1775_softc {
|
||||
struct pmac_therm sc_sensor;
|
||||
device_t sc_dev;
|
||||
struct intr_config_hook enum_hook;
|
||||
uint32_t sc_addr;
|
||||
};
|
||||
|
||||
/* Utility functions */
|
||||
static int ds1775_sensor_read(struct ds1775_softc *sc);
|
||||
static int ds1775_sensor_sysctl(SYSCTL_HANDLER_ARGS);
|
||||
static void ds1775_start(void *xdev);
|
||||
static int ds1775_read_2(device_t dev, uint32_t addr, uint8_t reg,
|
||||
uint16_t *data);
|
||||
|
||||
struct ds1775_softc {
|
||||
device_t sc_dev;
|
||||
struct intr_config_hook enum_hook;
|
||||
uint32_t sc_addr;
|
||||
struct ds1775_sensor *sc_sensors;
|
||||
|
||||
};
|
||||
static device_method_t ds1775_methods[] = {
|
||||
/* Device interface */
|
||||
DEVMETHOD(device_probe, ds1775_probe),
|
||||
|
@ -92,7 +90,6 @@ static driver_t ds1775_driver = {
|
|||
static devclass_t ds1775_devclass;
|
||||
|
||||
DRIVER_MODULE(ds1755, iicbus, ds1775_driver, ds1775_devclass, 0, 0);
|
||||
MALLOC_DEFINE(M_DS1775, "ds1775", "Temp-Monitor DS1775");
|
||||
|
||||
static int
|
||||
ds1775_read_2(device_t dev, uint32_t addr, uint8_t reg, uint16_t *data)
|
||||
|
@ -169,7 +166,6 @@ ds1775_start(void *xdev)
|
|||
{
|
||||
phandle_t child;
|
||||
struct ds1775_softc *sc;
|
||||
struct ds1775_sensor *sens;
|
||||
struct sysctl_oid *sensroot_oid;
|
||||
struct sysctl_ctx_list *ctx;
|
||||
ssize_t plen;
|
||||
|
@ -183,30 +179,34 @@ ds1775_start(void *xdev)
|
|||
|
||||
child = ofw_bus_get_node(dev);
|
||||
|
||||
sc->sc_sensors = malloc (sizeof(struct ds1775_sensor),
|
||||
M_DS1775, M_WAITOK | M_ZERO);
|
||||
|
||||
sens = sc->sc_sensors;
|
||||
|
||||
ctx = device_get_sysctl_ctx(dev);
|
||||
sensroot_oid = device_get_sysctl_tree(dev);
|
||||
|
||||
plen = OF_getprop(child, "hwsensor-location", sens->location,
|
||||
sizeof(sens->location));
|
||||
OF_getprop(child, "hwsensor-zone", &sc->sc_sensor.zone, sizeof(int));
|
||||
plen = OF_getprop(child, "hwsensor-location", sc->sc_sensor.name,
|
||||
sizeof(sc->sc_sensor.name));
|
||||
units = "C";
|
||||
|
||||
if (plen == -1) {
|
||||
strcpy(sysctl_name, "sensor");
|
||||
} else {
|
||||
for (i = 0; i < strlen(sens->location); i++) {
|
||||
sysctl_name[i] = tolower(sens->location[i]);
|
||||
for (i = 0; i < strlen(sc->sc_sensor.name); i++) {
|
||||
sysctl_name[i] = tolower(sc->sc_sensor.name[i]);
|
||||
if (isspace(sysctl_name[i]))
|
||||
sysctl_name[i] = '_';
|
||||
}
|
||||
sysctl_name[i] = 0;
|
||||
}
|
||||
|
||||
sprintf(sysctl_desc,"%s (%s)", sens->location, units);
|
||||
/* Make up target temperatures. These are low, for the drive bay. */
|
||||
sc->sc_sensor.target_temp = 300 + FCU_ZERO_C_TO_K;
|
||||
sc->sc_sensor.max_temp = 600 + FCU_ZERO_C_TO_K;
|
||||
|
||||
sc->sc_sensor.read =
|
||||
(int (*)(struct pmac_therm *sc))(ds1775_sensor_read);
|
||||
pmac_thermal_sensor_register(&sc->sc_sensor);
|
||||
|
||||
sprintf(sysctl_desc,"%s (%s)", sc->sc_sensor.name, units);
|
||||
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(sensroot_oid), OID_AUTO,
|
||||
sysctl_name,
|
||||
CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, dev,
|
||||
|
@ -216,14 +216,11 @@ ds1775_start(void *xdev)
|
|||
}
|
||||
|
||||
static int
|
||||
ds1775_sensor_read(device_t dev, struct ds1775_sensor *sens, int *temp)
|
||||
ds1775_sensor_read(struct ds1775_softc *sc)
|
||||
{
|
||||
struct ds1775_softc *sc;
|
||||
uint16_t buf[2];
|
||||
uint16_t read;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
|
||||
ds1775_read_2(sc->sc_dev, sc->sc_addr, DS1775_TEMP, buf);
|
||||
|
||||
read = *((int16_t *)buf);
|
||||
|
@ -231,29 +228,21 @@ ds1775_sensor_read(device_t dev, struct ds1775_sensor *sens, int *temp)
|
|||
/* The default mode of the ADC is 9 bit, the resolution is 0.5 C per
|
||||
bit. The temperature is in tenth kelvin.
|
||||
*/
|
||||
*temp = ((int16_t)(read) >> 7) * 5;
|
||||
|
||||
return (0);
|
||||
return (((int16_t)(read) >> 7) * 5 + FCU_ZERO_C_TO_K);
|
||||
}
|
||||
|
||||
static int
|
||||
ds1775_sensor_sysctl(SYSCTL_HANDLER_ARGS)
|
||||
{
|
||||
device_t dev;
|
||||
struct ds1775_softc *sc;
|
||||
struct ds1775_sensor *sens;
|
||||
int value;
|
||||
int error;
|
||||
unsigned int temp;
|
||||
|
||||
dev = arg1;
|
||||
sc = device_get_softc(dev);
|
||||
sens = &sc->sc_sensors[arg2];
|
||||
|
||||
error = ds1775_sensor_read(dev, sens, &value);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
|
||||
temp = value + FCU_ZERO_C_TO_K;
|
||||
temp = ds1775_sensor_read(sc);
|
||||
|
||||
error = sysctl_handle_int(oidp, &temp, 0, req);
|
||||
|
||||
|
|
|
@ -49,6 +49,7 @@ __FBSDID("$FreeBSD$");
|
|||
|
||||
#include <dev/ofw/openfirm.h>
|
||||
#include <dev/ofw/ofw_bus.h>
|
||||
#include <powerpc/powermac/powermac_thermal.h>
|
||||
|
||||
#define FCU_ZERO_C_TO_K 2732
|
||||
|
||||
|
@ -61,8 +62,10 @@ __FBSDID("$FreeBSD$");
|
|||
#define MAX6690_TEMP_MASK 0xe0
|
||||
|
||||
struct max6690_sensor {
|
||||
struct pmac_therm therm;
|
||||
device_t dev;
|
||||
|
||||
int id;
|
||||
char location[32];
|
||||
};
|
||||
|
||||
/* Regular bus attachment functions */
|
||||
|
@ -70,6 +73,7 @@ static int max6690_probe(device_t);
|
|||
static int max6690_attach(device_t);
|
||||
|
||||
/* Utility functions */
|
||||
static int max6690_sensor_read(struct max6690_sensor *sens);
|
||||
static int max6690_sensor_sysctl(SYSCTL_HANDLER_ARGS);
|
||||
static void max6690_start(void *xdev);
|
||||
static int max6690_read_1(device_t dev, uint32_t addr, uint8_t reg,
|
||||
|
@ -167,7 +171,7 @@ max6690_fill_sensor_prop(device_t dev)
|
|||
sizeof(location));
|
||||
while (len < prop_len) {
|
||||
if (sc->sc_sensors != NULL)
|
||||
strcpy(sc->sc_sensors[i].location, location + len);
|
||||
strcpy(sc->sc_sensors[i].therm.name, location + len);
|
||||
prev_len = strlen(location + len) + 1;
|
||||
len += prev_len;
|
||||
i++;
|
||||
|
@ -180,6 +184,22 @@ max6690_fill_sensor_prop(device_t dev)
|
|||
for (j = 0; j < i; j++)
|
||||
sc->sc_sensors[j].id = (id[j] & 0xf);
|
||||
|
||||
/* Fill the sensor zone property. */
|
||||
prop_len = OF_getprop(child, "hwsensor-zone", id, sizeof(id));
|
||||
for (j = 0; j < i; j++)
|
||||
sc->sc_sensors[j].therm.zone = id[j];
|
||||
|
||||
/* Set up remaining sensor properties */
|
||||
for (j = 0; j < i; j++) {
|
||||
sc->sc_sensors[j].dev = dev;
|
||||
|
||||
sc->sc_sensors[j].therm.target_temp = 400 + 2732;
|
||||
sc->sc_sensors[j].therm.max_temp = 800 + 2732;
|
||||
|
||||
sc->sc_sensors[j].therm.read =
|
||||
(int (*)(struct pmac_therm *))(max6690_sensor_read);
|
||||
}
|
||||
|
||||
return (i);
|
||||
}
|
||||
static int
|
||||
|
@ -240,10 +260,15 @@ max6690_start(void *xdev)
|
|||
/* Now we can fill the properties into the allocated struct. */
|
||||
sc->sc_nsensors = max6690_fill_sensor_prop(dev);
|
||||
|
||||
/* Register with powermac_thermal */
|
||||
for (i = 0; i < sc->sc_nsensors; i++)
|
||||
pmac_thermal_sensor_register(&sc->sc_sensors[i].therm);
|
||||
|
||||
/* Add sysctls for the sensors. */
|
||||
for (i = 0; i < sc->sc_nsensors; i++) {
|
||||
for (j = 0; j < strlen(sc->sc_sensors[i].location); j++) {
|
||||
sysctl_name[j] = tolower(sc->sc_sensors[i].location[j]);
|
||||
for (j = 0; j < strlen(sc->sc_sensors[i].therm.name); j++) {
|
||||
sysctl_name[j] =
|
||||
tolower(sc->sc_sensors[i].therm.name[j]);
|
||||
if (isspace(sysctl_name[j]))
|
||||
sysctl_name[j] = '_';
|
||||
}
|
||||
|
@ -265,7 +290,7 @@ max6690_start(void *xdev)
|
|||
device_printf(dev, "Sensors\n");
|
||||
for (i = 0; i < sc->sc_nsensors; i++) {
|
||||
device_printf(dev, "Location : %s ID: %d\n",
|
||||
sc->sc_sensors[i].location,
|
||||
sc->sc_sensors[i].therm.name,
|
||||
sc->sc_sensors[i].id);
|
||||
}
|
||||
}
|
||||
|
@ -274,14 +299,15 @@ max6690_start(void *xdev)
|
|||
}
|
||||
|
||||
static int
|
||||
max6690_sensor_read(device_t dev, struct max6690_sensor *sens, int *temp)
|
||||
max6690_sensor_read(struct max6690_sensor *sens)
|
||||
{
|
||||
uint8_t reg_int = 0, reg_ext = 0;
|
||||
uint8_t integer;
|
||||
uint8_t fraction;
|
||||
int temp;
|
||||
struct max6690_softc *sc;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
sc = device_get_softc(sens->dev);
|
||||
|
||||
/* The internal sensor id's are even, the external ar odd. */
|
||||
if ((sens->id % 2) == 0) {
|
||||
|
@ -301,9 +327,9 @@ max6690_sensor_read(device_t dev, struct max6690_sensor *sens, int *temp)
|
|||
/* The temperature is in tenth kelvin, the fractional part resolution
|
||||
is 0.125.
|
||||
*/
|
||||
*temp = (integer * 10) + (fraction >> 5) * 10 / 8;
|
||||
temp = (integer * 10) + (fraction >> 5) * 10 / 8;
|
||||
|
||||
return (0);
|
||||
return (temp);
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -320,9 +346,9 @@ max6690_sensor_sysctl(SYSCTL_HANDLER_ARGS)
|
|||
sc = device_get_softc(dev);
|
||||
sens = &sc->sc_sensors[arg2];
|
||||
|
||||
error = max6690_sensor_read(dev, sens, &value);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
value = max6690_sensor_read(sens);
|
||||
if (value < 0)
|
||||
return (EIO);
|
||||
|
||||
temp = value + FCU_ZERO_C_TO_K;
|
||||
|
||||
|
|
|
@ -49,6 +49,7 @@ __FBSDID("$FreeBSD$");
|
|||
|
||||
#include <dev/ofw/openfirm.h>
|
||||
#include <dev/ofw/ofw_bus.h>
|
||||
#include <powerpc/powermac/powermac_thermal.h>
|
||||
|
||||
/* FCU registers
|
||||
* /u3@0,f8000000/i2c@f8001000/fan@15e
|
||||
|
@ -66,10 +67,10 @@ __FBSDID("$FreeBSD$");
|
|||
#define FCU_PWM_SGET(x) 0x30 + (x) * 2 /* Set or get PWM. */
|
||||
|
||||
struct fcu_fan {
|
||||
struct pmac_fan fan;
|
||||
device_t dev;
|
||||
|
||||
int id;
|
||||
cell_t min;
|
||||
cell_t max;
|
||||
char location[32];
|
||||
enum {
|
||||
FCU_FAN_RPM,
|
||||
FCU_FAN_PWM
|
||||
|
@ -103,9 +104,9 @@ static int fcu_attach(device_t);
|
|||
/* Utility functions */
|
||||
static void fcu_attach_fans(device_t dev);
|
||||
static int fcu_fill_fan_prop(device_t dev);
|
||||
static int fcu_fan_set_rpm(device_t dev, struct fcu_fan *fan, int rpm);
|
||||
static int fcu_fan_get_rpm(device_t dev, struct fcu_fan *fan, int *rpm);
|
||||
static int fcu_fan_set_pwm(device_t dev, struct fcu_fan *fan, int pwm);
|
||||
static int fcu_fan_set_rpm(struct fcu_fan *fan, int rpm);
|
||||
static int fcu_fan_get_rpm(struct fcu_fan *fan);
|
||||
static int fcu_fan_set_pwm(struct fcu_fan *fan, int pwm);
|
||||
static int fcu_fan_get_pwm(device_t dev, struct fcu_fan *fan, int *pwm,
|
||||
int *rpm);
|
||||
static int fcu_fanrpm_sysctl(SYSCTL_HANDLER_ARGS);
|
||||
|
@ -249,23 +250,23 @@ fcu_start(void *xdev)
|
|||
}
|
||||
|
||||
static int
|
||||
fcu_fan_set_rpm(device_t dev, struct fcu_fan *fan, int rpm)
|
||||
fcu_fan_set_rpm(struct fcu_fan *fan, int rpm)
|
||||
{
|
||||
uint8_t reg;
|
||||
struct fcu_softc *sc;
|
||||
unsigned char buf[2];
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
sc = device_get_softc(fan->dev);
|
||||
|
||||
/* Clamp to allowed range */
|
||||
rpm = max(fan->min, rpm);
|
||||
rpm = min(fan->max, rpm);
|
||||
rpm = max(fan->fan.min_rpm, rpm);
|
||||
rpm = min(fan->fan.max_rpm, rpm);
|
||||
|
||||
if (fan->type == FCU_FAN_RPM) {
|
||||
reg = FCU_RPM_SET(fan->id);
|
||||
fan->setpoint = rpm;
|
||||
} else {
|
||||
device_printf(dev, "Unknown fan type: %d\n", fan->type);
|
||||
device_printf(fan->dev, "Unknown fan type: %d\n", fan->type);
|
||||
return (EIO);
|
||||
}
|
||||
|
||||
|
@ -278,66 +279,68 @@ fcu_fan_set_rpm(device_t dev, struct fcu_fan *fan, int rpm)
|
|||
}
|
||||
|
||||
static int
|
||||
fcu_fan_get_rpm(device_t dev, struct fcu_fan *fan, int *rpm)
|
||||
fcu_fan_get_rpm(struct fcu_fan *fan)
|
||||
{
|
||||
uint8_t reg;
|
||||
struct fcu_softc *sc;
|
||||
uint8_t buff[2] = { 0, 0 };
|
||||
uint8_t active = 0, avail = 0, fail = 0;
|
||||
int rpm;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
sc = device_get_softc(fan->dev);
|
||||
|
||||
if (fan->type == FCU_FAN_RPM) {
|
||||
/* Check if the fan is available. */
|
||||
reg = FCU_RPM_AVAILABLE;
|
||||
fcu_read_1(sc->sc_dev, sc->sc_addr, reg, &avail);
|
||||
if ((avail & (1 << fan->id)) == 0) {
|
||||
device_printf(dev, "RPM Fan not available ID: %d\n",
|
||||
fan->id);
|
||||
return (EIO);
|
||||
device_printf(fan->dev,
|
||||
"RPM Fan not available ID: %d\n", fan->id);
|
||||
return (-1);
|
||||
}
|
||||
/* Check if we have a failed fan. */
|
||||
reg = FCU_RPM_FAIL;
|
||||
fcu_read_1(sc->sc_dev, sc->sc_addr, reg, &fail);
|
||||
if ((fail & (1 << fan->id)) != 0) {
|
||||
device_printf(dev, "RPM Fan failed ID: %d\n", fan->id);
|
||||
return (EIO);
|
||||
device_printf(fan->dev,
|
||||
"RPM Fan failed ID: %d\n", fan->id);
|
||||
return (-1);
|
||||
}
|
||||
/* Check if fan is active. */
|
||||
reg = FCU_RPM_ACTIVE;
|
||||
fcu_read_1(sc->sc_dev, sc->sc_addr, reg, &active);
|
||||
if ((active & (1 << fan->id)) == 0) {
|
||||
device_printf(dev, "RPM Fan not active ID: %d\n",
|
||||
device_printf(fan->dev, "RPM Fan not active ID: %d\n",
|
||||
fan->id);
|
||||
return (ENXIO);
|
||||
return (-1);
|
||||
}
|
||||
reg = FCU_RPM_READ(fan->id);
|
||||
|
||||
} else {
|
||||
device_printf(dev, "Unknown fan type: %d\n", fan->type);
|
||||
return (EIO);
|
||||
device_printf(fan->dev, "Unknown fan type: %d\n", fan->type);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/* It seems that we can read the fans rpm. */
|
||||
fcu_read_1(sc->sc_dev, sc->sc_addr, reg, buff);
|
||||
|
||||
*rpm = (buff[0] << (8 - fcu_rpm_shift)) | buff[1] >> fcu_rpm_shift;
|
||||
rpm = (buff[0] << (8 - fcu_rpm_shift)) | buff[1] >> fcu_rpm_shift;
|
||||
|
||||
return (0);
|
||||
return (rpm);
|
||||
}
|
||||
|
||||
static int
|
||||
fcu_fan_set_pwm(device_t dev, struct fcu_fan *fan, int pwm)
|
||||
fcu_fan_set_pwm(struct fcu_fan *fan, int pwm)
|
||||
{
|
||||
uint8_t reg;
|
||||
struct fcu_softc *sc;
|
||||
uint8_t buf[2];
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
sc = device_get_softc(fan->dev);
|
||||
|
||||
/* Clamp to allowed range */
|
||||
pwm = max(fan->min, pwm);
|
||||
pwm = min(fan->max, pwm);
|
||||
pwm = max(fan->fan.min_rpm, pwm);
|
||||
pwm = min(fan->fan.max_rpm, pwm);
|
||||
|
||||
if (fan->type == FCU_FAN_PWM) {
|
||||
reg = FCU_PWM_SGET(fan->id);
|
||||
|
@ -347,7 +350,7 @@ fcu_fan_set_pwm(device_t dev, struct fcu_fan *fan, int pwm)
|
|||
pwm = 30;
|
||||
fan->setpoint = pwm;
|
||||
} else {
|
||||
device_printf(dev, "Unknown fan type: %d\n", fan->type);
|
||||
device_printf(fan->dev, "Unknown fan type: %d\n", fan->type);
|
||||
return (EIO);
|
||||
}
|
||||
|
||||
|
@ -434,7 +437,7 @@ fcu_fill_fan_prop(device_t dev)
|
|||
sizeof(location));
|
||||
while (len < prop_len) {
|
||||
if (sc->sc_fans != NULL) {
|
||||
strcpy(sc->sc_fans[i].location, location + len);
|
||||
strcpy(sc->sc_fans[i].fan.name, location + len);
|
||||
}
|
||||
prev_len = strlen(location + len) + 1;
|
||||
len += prev_len;
|
||||
|
@ -463,6 +466,33 @@ fcu_fill_fan_prop(device_t dev)
|
|||
for (j = 0; j < i; j++)
|
||||
sc->sc_fans[j].id = ((id[j] >> 8) & 0x0f) % 8;
|
||||
|
||||
/* Fill the fan zone property. */
|
||||
prop_len = OF_getprop(child, "hwctrl-zone", id, sizeof(id));
|
||||
for (j = 0; j < i; j++)
|
||||
sc->sc_fans[j].fan.zone = id[j];
|
||||
|
||||
/* Finish setting up fan properties */
|
||||
for (j = 0; j < i; j++) {
|
||||
sc->sc_fans[j].dev = sc->sc_dev;
|
||||
if (sc->sc_fans[j].type == FCU_FAN_RPM) {
|
||||
sc->sc_fans[j].fan.min_rpm = 4800 >> fcu_rpm_shift;
|
||||
sc->sc_fans[j].fan.max_rpm = 56000 >> fcu_rpm_shift;
|
||||
sc->sc_fans[j].setpoint =
|
||||
fcu_fan_get_rpm(&sc->sc_fans[j]);
|
||||
sc->sc_fans[j].fan.read =
|
||||
(int (*)(struct pmac_fan *))(fcu_fan_get_rpm);
|
||||
sc->sc_fans[j].fan.set =
|
||||
(int (*)(struct pmac_fan *, int))(fcu_fan_set_rpm);
|
||||
} else {
|
||||
sc->sc_fans[j].fan.min_rpm = 40; /* Percent */
|
||||
sc->sc_fans[j].fan.max_rpm = 100;
|
||||
sc->sc_fans[j].fan.read = NULL;
|
||||
sc->sc_fans[j].fan.set =
|
||||
(int (*)(struct pmac_fan *, int))(fcu_fan_set_pwm);
|
||||
}
|
||||
sc->sc_fans[j].fan.default_rpm = sc->sc_fans[j].fan.max_rpm;
|
||||
}
|
||||
|
||||
return (i);
|
||||
}
|
||||
|
||||
|
@ -478,7 +508,7 @@ fcu_fanrpm_sysctl(SYSCTL_HANDLER_ARGS)
|
|||
sc = device_get_softc(fcu);
|
||||
fan = &sc->sc_fans[arg2 & 0x00ff];
|
||||
if (fan->type == FCU_FAN_RPM) {
|
||||
fcu_fan_get_rpm(fcu, fan, &rpm);
|
||||
rpm = fcu_fan_get_rpm(fan);
|
||||
error = sysctl_handle_int(oidp, &rpm, 0, req);
|
||||
} else {
|
||||
fcu_fan_get_pwm(fcu, fan, &pwm, &rpm);
|
||||
|
@ -504,9 +534,9 @@ fcu_fanrpm_sysctl(SYSCTL_HANDLER_ARGS)
|
|||
return (error);
|
||||
|
||||
if (fan->type == FCU_FAN_RPM)
|
||||
return (fcu_fan_set_rpm(fcu, fan, rpm));
|
||||
return (fcu_fan_set_rpm(fan, rpm));
|
||||
else
|
||||
return (fcu_fan_set_pwm(fcu, fan, pwm));
|
||||
return (fcu_fan_set_pwm(fan, pwm));
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -543,39 +573,36 @@ fcu_attach_fans(device_t dev)
|
|||
/* Now we can fill the properties into the allocated struct. */
|
||||
sc->sc_nfans = fcu_fill_fan_prop(dev);
|
||||
|
||||
/* Register fans with pmac_thermal */
|
||||
for (i = 0; i < sc->sc_nfans; i++)
|
||||
pmac_thermal_fan_register(&sc->sc_fans[i].fan);
|
||||
|
||||
/* Add sysctls for the fans. */
|
||||
for (i = 0; i < sc->sc_nfans; i++) {
|
||||
for (j = 0; j < strlen(sc->sc_fans[i].location); j++) {
|
||||
sysctl_name[j] = tolower(sc->sc_fans[i].location[j]);
|
||||
for (j = 0; j < strlen(sc->sc_fans[i].fan.name); j++) {
|
||||
sysctl_name[j] = tolower(sc->sc_fans[i].fan.name[j]);
|
||||
if (isspace(sysctl_name[j]))
|
||||
sysctl_name[j] = '_';
|
||||
}
|
||||
sysctl_name[j] = 0;
|
||||
|
||||
if (sc->sc_fans[i].type == FCU_FAN_RPM) {
|
||||
sc->sc_fans[i].min = 2400 >> fcu_rpm_shift;
|
||||
sc->sc_fans[i].max = 56000 >> fcu_rpm_shift;
|
||||
fcu_fan_get_rpm(dev, &sc->sc_fans[i],
|
||||
&sc->sc_fans[i].setpoint);
|
||||
|
||||
oid = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(fanroot_oid),
|
||||
OID_AUTO, sysctl_name,
|
||||
CTLFLAG_RD, 0, "Fan Information");
|
||||
SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
|
||||
"minrpm", CTLTYPE_INT | CTLFLAG_RD,
|
||||
&(sc->sc_fans[i].min), sizeof(cell_t),
|
||||
"Minimum allowed RPM");
|
||||
&(sc->sc_fans[i].fan.min_rpm),
|
||||
sizeof(int), "Minimum allowed RPM");
|
||||
SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
|
||||
"maxrpm", CTLTYPE_INT | CTLFLAG_RD,
|
||||
&(sc->sc_fans[i].max), sizeof(cell_t),
|
||||
"Maximum allowed RPM");
|
||||
&(sc->sc_fans[i].fan.max_rpm),
|
||||
sizeof(int), "Maximum allowed RPM");
|
||||
/* I use i to pass the fan id. */
|
||||
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
|
||||
"rpm", CTLTYPE_INT | CTLFLAG_RW, dev, i,
|
||||
fcu_fanrpm_sysctl, "I", "Fan RPM");
|
||||
} else {
|
||||
sc->sc_fans[i].min = 30;
|
||||
sc->sc_fans[i].max = 100;
|
||||
fcu_fan_get_pwm(dev, &sc->sc_fans[i],
|
||||
&sc->sc_fans[i].setpoint,
|
||||
&sc->sc_fans[i].rpm);
|
||||
|
@ -585,12 +612,12 @@ fcu_attach_fans(device_t dev)
|
|||
CTLFLAG_RD, 0, "Fan Information");
|
||||
SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
|
||||
"minpwm", CTLTYPE_INT | CTLFLAG_RD,
|
||||
&(sc->sc_fans[i].min), sizeof(cell_t),
|
||||
"Minimum allowed PWM in %");
|
||||
&(sc->sc_fans[i].fan.min_rpm),
|
||||
sizeof(int), "Minimum allowed PWM in %");
|
||||
SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
|
||||
"maxpwm", CTLTYPE_INT | CTLFLAG_RD,
|
||||
&(sc->sc_fans[i].max), sizeof(cell_t),
|
||||
"Maximum allowed PWM in %");
|
||||
&(sc->sc_fans[i].fan.max_rpm),
|
||||
sizeof(int), "Maximum allowed PWM in %");
|
||||
/* I use i to pass the fan id or'ed with the type
|
||||
* of info I want to display/modify.
|
||||
*/
|
||||
|
@ -610,7 +637,7 @@ fcu_attach_fans(device_t dev)
|
|||
device_printf(dev, "Fans\n");
|
||||
for (i = 0; i < sc->sc_nfans; i++) {
|
||||
device_printf(dev, "Location: %s type: %d ID: %d "
|
||||
"RPM: %d\n", sc->sc_fans[i].location,
|
||||
"RPM: %d\n", sc->sc_fans[i].fan.name,
|
||||
sc->sc_fans[i].type, sc->sc_fans[i].id,
|
||||
(sc->sc_fans[i].type == FCU_FAN_RPM) ?
|
||||
sc->sc_fans[i].setpoint :
|
||||
|
|
Loading…
Reference in a new issue