summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorandreast <andreast@FreeBSD.org>2011-01-11 21:18:29 +0000
committerandreast <andreast@FreeBSD.org>2011-01-11 21:18:29 +0000
commita13f07741ca203fff332d6336ddc469e09546483 (patch)
tree5dffb7e1fb0ee88e49bd266c2cb897d4be5ebac5
parentcc2333f8c165c12709c341e07e098dd48eb7ecaf (diff)
downloadFreeBSD-src-a13f07741ca203fff332d6336ddc469e09546483.zip
FreeBSD-src-a13f07741ca203fff332d6336ddc469e09546483.tar.gz
Add new functions, fcu_fan_set_pwm and fcu_fan_get_pwm, to set and get
the pwm values. We can now set the fan's speed of a PWM controlled fan with % numbers between 30 and 100 % instead of trying to model a % number based on rpm. The fcu chip offers both, the dutycycle and the rpm value of the PWM controlled fans. I added the rpm value to the list of information available via sysctl(8). Tested by: Paul Mather <paul at gromit dlib vt edu> Approved by: nwhitehorn (mentor)
-rw-r--r--sys/powerpc/powermac/fcu.c230
1 files changed, 172 insertions, 58 deletions
diff --git a/sys/powerpc/powermac/fcu.c b/sys/powerpc/powermac/fcu.c
index 7318f1e..004b4db 100644
--- a/sys/powerpc/powermac/fcu.c
+++ b/sys/powerpc/powermac/fcu.c
@@ -62,19 +62,20 @@ __FBSDID("$FreeBSD$");
#define FCU_PWM_FAIL 0x2b
#define FCU_PWM_AVAILABLE 0x2c
#define FCU_PWM_ACTIVE 0x2d
-#define FCU_PWM_READ(x) 0x31 + (x) * 2
-#define FCU_PWM_SET(x) 0x30 + (x) * 2
+#define FCU_PWM_RPM(x) 0x31 + (x) * 2 /* Get RPM. */
+#define FCU_PWM_SGET(x) 0x30 + (x) * 2 /* Set or get PWM. */
struct fcu_fan {
int id;
- cell_t min_rpm;
- cell_t max_rpm;
+ cell_t min;
+ cell_t max;
char location[32];
enum {
FCU_FAN_RPM,
FCU_FAN_PWM
} type;
int setpoint;
+ int rpm;
};
struct fcu_softc {
@@ -85,6 +86,14 @@ struct fcu_softc {
int sc_nfans;
};
+/* We can read the PWM and the RPM from a PWM controlled fan.
+ * Offer both values via sysctl.
+ */
+enum {
+ FCU_PWM_SYSCTL_PWM = 1 << 8,
+ FCU_PWM_SYSCTL_RPM = 2 << 8
+};
+
static int fcu_rpm_shift;
/* Regular bus attachment functions */
@@ -96,6 +105,9 @@ 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_get_pwm(device_t dev, struct fcu_fan *fan, int *pwm,
+ int *rpm);
static int fcu_fanrpm_sysctl(SYSCTL_HANDLER_ARGS);
static void fcu_start(void *xdev);
static int fcu_write(device_t dev, uint32_t addr, uint8_t reg, uint8_t *buf,
@@ -246,34 +258,21 @@ fcu_fan_set_rpm(device_t dev, struct fcu_fan *fan, int rpm)
sc = device_get_softc(dev);
/* Clamp to allowed range */
- rpm = max(fan->min_rpm, rpm);
- rpm = min(fan->max_rpm, rpm);
+ rpm = max(fan->min, rpm);
+ rpm = min(fan->max, rpm);
if (fan->type == FCU_FAN_RPM) {
reg = FCU_RPM_SET(fan->id);
fan->setpoint = rpm;
- } else if (fan->type == FCU_FAN_PWM) {
- reg = FCU_PWM_SET(fan->id);
- if (rpm > 3500)
- rpm = 3500;
- if (rpm < 500)
- rpm = 500;
- fan->setpoint = rpm;
- /* PWM 30: 550 rpm, PWM 255: 3400 rpm. */
- rpm = (rpm * 255) / 3500;
} else {
device_printf(dev, "Unknown fan type: %d\n", fan->type);
return (EIO);
}
- if (fan->type == FCU_FAN_RPM) {
- buf[0] = rpm >> (8 - fcu_rpm_shift);
- buf[1] = rpm << fcu_rpm_shift;
- fcu_write(sc->sc_dev, sc->sc_addr, reg, buf, 2);
- } else {
- buf[0] = rpm;
- fcu_write(sc->sc_dev, sc->sc_addr, reg, buf, 1);
- }
+ buf[0] = rpm >> (8 - fcu_rpm_shift);
+ buf[1] = rpm << fcu_rpm_shift;
+
+ fcu_write(sc->sc_dev, sc->sc_addr, reg, buf, 2);
return (0);
}
@@ -313,7 +312,63 @@ fcu_fan_get_rpm(device_t dev, struct fcu_fan *fan, int *rpm)
return (ENXIO);
}
reg = FCU_RPM_READ(fan->id);
- } else if (fan->type == FCU_FAN_PWM) {
+
+ } else {
+ device_printf(dev, "Unknown fan type: %d\n", fan->type);
+ return (EIO);
+ }
+
+ /* 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;
+
+ return (0);
+}
+
+static int
+fcu_fan_set_pwm(device_t dev, struct fcu_fan *fan, int pwm)
+{
+ uint8_t reg;
+ struct fcu_softc *sc;
+ uint8_t buf[2];
+
+ sc = device_get_softc(dev);
+
+ /* Clamp to allowed range */
+ pwm = max(fan->min, pwm);
+ pwm = min(fan->max, pwm);
+
+ if (fan->type == FCU_FAN_PWM) {
+ reg = FCU_PWM_SGET(fan->id);
+ if (pwm > 100)
+ pwm = 100;
+ if (pwm < 30)
+ pwm = 30;
+ fan->setpoint = pwm;
+ } else {
+ device_printf(dev, "Unknown fan type: %d\n", fan->type);
+ return (EIO);
+ }
+
+ buf[0] = (pwm * 2550) / 1000;
+
+ fcu_write(sc->sc_dev, sc->sc_addr, reg, buf, 1);
+
+ return (0);
+}
+
+static int
+fcu_fan_get_pwm(device_t dev, struct fcu_fan *fan, int *pwm, int *rpm)
+{
+ uint8_t reg;
+ struct fcu_softc *sc;
+ uint8_t buf[2];
+ uint8_t active = 0, avail = 0, fail = 0;
+
+ sc = device_get_softc(dev);
+
+ if (fan->type == FCU_FAN_PWM) {
/* Check if the fan is available. */
reg = FCU_PWM_AVAILABLE;
fcu_read_1(sc->sc_dev, sc->sc_addr, reg, &avail);
@@ -337,16 +392,21 @@ fcu_fan_get_rpm(device_t dev, struct fcu_fan *fan, int *rpm)
fan->id);
return (ENXIO);
}
- reg = FCU_PWM_READ(fan->id);
+ reg = FCU_PWM_SGET(fan->id);
} else {
device_printf(dev, "Unknown fan type: %d\n", fan->type);
return (EIO);
}
- /* It seems that we can read the fans rpm. */
- fcu_read_1(sc->sc_dev, sc->sc_addr, reg, buff);
+ /* It seems that we can read the fans pwm. */
+ fcu_read_1(sc->sc_dev, sc->sc_addr, reg, buf);
- *rpm = (buff[0] << (8 - fcu_rpm_shift)) | buff[1] >> fcu_rpm_shift;
+ *pwm = (buf[0] * 1000) / 2550;
+
+ /* Now read the rpm. */
+ reg = FCU_PWM_RPM(fan->id);
+ fcu_read_1(sc->sc_dev, sc->sc_addr, reg, buf);
+ *rpm = (buf[0] << (8 - fcu_rpm_shift)) | buf[1] >> fcu_rpm_shift;
return (0);
}
@@ -412,18 +472,41 @@ fcu_fanrpm_sysctl(SYSCTL_HANDLER_ARGS)
device_t fcu;
struct fcu_softc *sc;
struct fcu_fan *fan;
- int rpm = 0, error;
+ int rpm = 0, pwm = 0, error;
fcu = arg1;
sc = device_get_softc(fcu);
- fan = &sc->sc_fans[arg2];
- fcu_fan_get_rpm(fcu, fan, &rpm);
- error = sysctl_handle_int(oidp, &rpm, 0, req);
+ fan = &sc->sc_fans[arg2 & 0x00ff];
+ if (fan->type == FCU_FAN_RPM) {
+ fcu_fan_get_rpm(fcu, fan, &rpm);
+ error = sysctl_handle_int(oidp, &rpm, 0, req);
+ } else {
+ fcu_fan_get_pwm(fcu, fan, &pwm, &rpm);
+
+ switch (arg2 & 0xff00) {
+ case FCU_PWM_SYSCTL_PWM:
+ error = sysctl_handle_int(oidp, &pwm, 0, req);
+ break;
+ case FCU_PWM_SYSCTL_RPM:
+ error = sysctl_handle_int(oidp, &rpm, 0, req);
+ break;
+ default:
+ /* This should never happen */
+ error = -1;
+ };
+ }
+
+ /* We can only read the RPM from a PWM controlled fan, so return. */
+ if ((arg2 & 0xff00) == FCU_PWM_SYSCTL_RPM)
+ return (0);
if (error || !req->newptr)
return (error);
- return (fcu_fan_set_rpm(fcu, fan, rpm));
+ if (fan->type == FCU_FAN_RPM)
+ return (fcu_fan_set_rpm(fcu, fan, rpm));
+ else
+ return (fcu_fan_set_pwm(fcu, fan, pwm));
}
static void
@@ -432,7 +515,6 @@ fcu_attach_fans(device_t dev)
struct fcu_softc *sc;
struct sysctl_oid *oid, *fanroot_oid;
struct sysctl_ctx_list *ctx;
- phandle_t child;
char sysctl_name[32];
int i, j;
@@ -440,8 +522,6 @@ fcu_attach_fans(device_t dev)
sc->sc_nfans = 0;
- child = ofw_bus_get_node(dev);
-
/* Count the actual number of fans. */
sc->sc_nfans = fcu_fill_fan_prop(dev);
@@ -472,35 +552,69 @@ fcu_attach_fans(device_t dev)
}
sysctl_name[j] = 0;
- sc->sc_fans[i].min_rpm = 2400 >> fcu_rpm_shift;
- sc->sc_fans[i].max_rpm = 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_rpm), sizeof(cell_t),
- "Minimum allowed RPM");
- SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "maxrpm",
- CTLTYPE_INT | CTLFLAG_RD,
- &(sc->sc_fans[i].max_rpm), sizeof(cell_t),
- "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");
+ 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");
+ 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");
+ /* 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);
+
+ 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,
+ "minpwm", CTLTYPE_INT | CTLFLAG_RD,
+ &(sc->sc_fans[i].min), sizeof(cell_t),
+ "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 %");
+ /* I use i to pass the fan id or'ed with the type
+ * of info I want to display/modify.
+ */
+ SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
+ "pwm", CTLTYPE_INT | CTLFLAG_RW, dev,
+ FCU_PWM_SYSCTL_PWM | i,
+ fcu_fanrpm_sysctl, "I", "Fan PWM in %");
+ SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
+ "rpm", CTLTYPE_INT | CTLFLAG_RD, dev,
+ FCU_PWM_SYSCTL_RPM | i,
+ fcu_fanrpm_sysctl, "I", "Fan RPM");
+ }
}
/* Dump fan location, type & RPM. */
if (bootverbose) {
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,
+ device_printf(dev, "Location: %s type: %d ID: %d "
+ "RPM: %d\n", sc->sc_fans[i].location,
sc->sc_fans[i].type, sc->sc_fans[i].id,
- sc->sc_fans[i].setpoint);
+ (sc->sc_fans[i].type == FCU_FAN_RPM) ?
+ sc->sc_fans[i].setpoint :
+ sc->sc_fans[i].rpm );
}
}
}
OpenPOWER on IntegriCloud