From owner-svn-src-head@FreeBSD.ORG Sun Mar 4 08:43:33 2012 Return-Path: Delivered-To: svn-src-head@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [69.147.83.52]) by hub.freebsd.org (Postfix) with ESMTP id A0EB91065674; Sun, 4 Mar 2012 08:43:33 +0000 (UTC) (envelope-from andreast@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id 8E8518FC12; Sun, 4 Mar 2012 08:43:33 +0000 (UTC) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.4/8.14.4) with ESMTP id q248hXfa022389; Sun, 4 Mar 2012 08:43:33 GMT (envelope-from andreast@svn.freebsd.org) Received: (from andreast@localhost) by svn.freebsd.org (8.14.4/8.14.4/Submit) id q248hXVf022387; Sun, 4 Mar 2012 08:43:33 GMT (envelope-from andreast@svn.freebsd.org) Message-Id: <201203040843.q248hXVf022387@svn.freebsd.org> From: Andreas Tobler Date: Sun, 4 Mar 2012 08:43:33 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r232482 - head/sys/powerpc/powermac X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 04 Mar 2012 08:43:33 -0000 Author: andreast Date: Sun Mar 4 08:43:33 2012 New Revision: 232482 URL: http://svn.freebsd.org/changeset/base/232482 Log: Add support for PWM controlled fans. I found these fans on my PowerMac9,1. These fans are not located under the same node as the the RPM controlled ones, So I had to adapt the current source to parse and fill the properties correctly. To control the fans we can set the PWM ratio via sysctl between 20 and 100%. Tested by: nwhitehorn MFC after: 3 weeks Modified: head/sys/powerpc/powermac/smu.c Modified: head/sys/powerpc/powermac/smu.c ============================================================================== --- head/sys/powerpc/powermac/smu.c Sun Mar 4 07:29:35 2012 (r232481) +++ head/sys/powerpc/powermac/smu.c Sun Mar 4 08:43:33 2012 (r232482) @@ -74,8 +74,21 @@ struct smu_fan { device_t dev; cell_t reg; + enum { + SMU_FAN_RPM, + SMU_FAN_PWM + } type; int old_style; int setpoint; + int rpm; +}; + +/* We can read the PWM and the RPM from a PWM controlled fan. + * Offer both values via sysctl. + */ +enum { + SMU_PWM_SYSCTL_PWM = 1 << 8, + SMU_PWM_SYSCTL_RPM = 2 << 8 }; struct smu_sensor { @@ -205,6 +218,10 @@ static MALLOC_DEFINE(M_SMU, "smu", "SMU /* Command types */ #define SMU_ADC 0xd8 #define SMU_FAN 0x4a +#define SMU_RPM_STATUS 0x01 +#define SMU_RPM_SETPOINT 0x02 +#define SMU_PWM_STATUS 0x11 +#define SMU_PWM_SETPOINT 0x12 #define SMU_I2C 0x9a #define SMU_I2C_SIMPLE 0x00 #define SMU_I2C_NORMAL 0x01 @@ -303,19 +320,21 @@ smu_attach(device_t dev) EVENTHANDLER_REGISTER(cpufreq_post_change, smu_cpufreq_post_change, dev, EVENTHANDLER_PRI_ANY); + node = ofw_bus_get_node(dev); + + /* Some SMUs have RPM and PWM controlled fans which do not sit + * under the same node. So we have to attach them separately. + */ + smu_attach_fans(dev, node); + /* - * Detect and attach child devices. + * Now detect and attach the other child devices. */ - node = ofw_bus_get_node(dev); for (child = OF_child(node); child != 0; child = OF_peer(child)) { char name[32]; memset(name, 0, sizeof(name)); OF_getprop(child, "name", name, sizeof(name)); - if (strncmp(name, "rpm-fans", 9) == 0 || - strncmp(name, "fans", 5) == 0) - smu_attach_fans(dev, child); - if (strncmp(name, "sensors", 8) == 0) smu_attach_sensors(dev, child); @@ -660,7 +679,7 @@ smu_fan_set_rpm(struct smu_fan *fan, int cmd.data[1] = fan->reg; cmd.data[2] = (rpm >> 8) & 0xff; cmd.data[3] = rpm & 0xff; - + error = smu_run_cmd(smu, &cmd, 1); if (error && error != EWOULDBLOCK) fan->old_style = 1; @@ -668,7 +687,7 @@ smu_fan_set_rpm(struct smu_fan *fan, int if (fan->old_style) { cmd.len = 14; - cmd.data[0] = 0; + cmd.data[0] = 0x00; /* RPM fan. */ cmd.data[1] = 1 << fan->reg; cmd.data[2 + 2*fan->reg] = (rpm >> 8) & 0xff; cmd.data[3 + 2*fan->reg] = rpm & 0xff; @@ -704,7 +723,7 @@ smu_fan_read_rpm(struct smu_fan *fan) if (fan->old_style) { cmd.cmd = SMU_FAN; cmd.len = 1; - cmd.data[0] = 1; + cmd.data[0] = SMU_RPM_STATUS; error = smu_run_cmd(smu, &cmd, 1); if (error) @@ -715,6 +734,98 @@ smu_fan_read_rpm(struct smu_fan *fan) return (rpm); } +static int +smu_fan_set_pwm(struct smu_fan *fan, int pwm) +{ + device_t smu = fan->dev; + struct smu_cmd cmd; + int error; + + cmd.cmd = SMU_FAN; + error = EIO; + + /* Clamp to allowed range */ + pwm = max(fan->fan.min_rpm, pwm); + pwm = min(fan->fan.max_rpm, pwm); + + /* + * Apple has two fan control mechanisms. We can't distinguish + * them except by seeing if the new one fails. If the new one + * fails, use the old one. + */ + + if (!fan->old_style) { + cmd.len = 4; + cmd.data[0] = 0x30; + cmd.data[1] = fan->reg; + cmd.data[2] = (pwm >> 8) & 0xff; + cmd.data[3] = pwm & 0xff; + + error = smu_run_cmd(smu, &cmd, 1); + if (error && error != EWOULDBLOCK) + fan->old_style = 1; + } + + if (fan->old_style) { + cmd.len = 14; + cmd.data[0] = 0x10; /* PWM fan. */ + cmd.data[1] = 1 << fan->reg; + cmd.data[2 + 2*fan->reg] = (pwm >> 8) & 0xff; + cmd.data[3 + 2*fan->reg] = pwm & 0xff; + error = smu_run_cmd(smu, &cmd, 1); + } + + if (error == 0) + fan->setpoint = pwm; + + return (error); +} + +static int +smu_fan_read_pwm(struct smu_fan *fan, int *pwm, int *rpm) +{ + device_t smu = fan->dev; + struct smu_cmd cmd; + int error; + + if (!fan->old_style) { + cmd.cmd = SMU_FAN; + cmd.len = 2; + cmd.data[0] = 0x31; + cmd.data[1] = fan->reg; + + error = smu_run_cmd(smu, &cmd, 1); + if (error && error != EWOULDBLOCK) + fan->old_style = 1; + + *rpm = (cmd.data[0] << 8) | cmd.data[1]; + } + + if (fan->old_style) { + cmd.cmd = SMU_FAN; + cmd.len = 1; + cmd.data[0] = SMU_PWM_STATUS; + + error = smu_run_cmd(smu, &cmd, 1); + if (error) + return (error); + + *rpm = (cmd.data[fan->reg*2+1] << 8) | cmd.data[fan->reg*2+2]; + } + if (fan->old_style) { + cmd.cmd = SMU_FAN; + cmd.len = 14; + cmd.data[0] = SMU_PWM_SETPOINT; + cmd.data[1] = 1 << fan->reg; + + error = smu_run_cmd(smu, &cmd, 1); + if (error) + return (error); + + *pwm = cmd.data[fan->reg*2+2]; + } + return (0); +} static int smu_fanrpm_sysctl(SYSCTL_HANDLER_ARGS) @@ -722,24 +833,127 @@ smu_fanrpm_sysctl(SYSCTL_HANDLER_ARGS) device_t smu; struct smu_softc *sc; struct smu_fan *fan; - int rpm, error; + int pwm = 0, rpm, error = 0; smu = arg1; sc = device_get_softc(smu); - fan = &sc->sc_fans[arg2]; + fan = &sc->sc_fans[arg2 & 0xff]; - rpm = smu_fan_read_rpm(fan); - if (rpm < 0) - return (rpm); + if (fan->type == SMU_FAN_RPM) { + rpm = smu_fan_read_rpm(fan); + if (rpm < 0) + return (rpm); - error = sysctl_handle_int(oidp, &rpm, 0, req); + error = sysctl_handle_int(oidp, &rpm, 0, req); + } else { + error = smu_fan_read_pwm(fan, &pwm, &rpm); + if (error < 0) + return (EIO); + + switch (arg2 & 0xff00) { + case SMU_PWM_SYSCTL_PWM: + error = sysctl_handle_int(oidp, &pwm, 0, req); + break; + case SMU_PWM_SYSCTL_RPM: + error = sysctl_handle_int(oidp, &rpm, 0, req); + break; + default: + /* This should never happen */ + return (EINVAL); + }; + } + /* We can only read the RPM from a PWM controlled fan, so return. */ + if ((arg2 & 0xff00) == SMU_PWM_SYSCTL_RPM) + return (0); if (error || !req->newptr) return (error); sc->sc_lastuserchange = time_uptime; - return (smu_fan_set_rpm(fan, rpm)); + if (fan->type == SMU_FAN_RPM) + return (smu_fan_set_rpm(fan, rpm)); + else + return (smu_fan_set_pwm(fan, pwm)); +} + +static void +smu_fill_fan_prop(device_t dev, phandle_t child, int id) +{ + struct smu_fan *fan; + struct smu_softc *sc; + char type[32]; + + sc = device_get_softc(dev); + fan = &sc->sc_fans[id]; + + OF_getprop(child, "device_type", type, sizeof(type)); + /* We have either RPM or PWM controlled fans. */ + if (strcmp(type, "fan-rpm-control") == 0) + fan->type = SMU_FAN_RPM; + else + fan->type = SMU_FAN_PWM; + + fan->dev = dev; + fan->old_style = 0; + OF_getprop(child, "reg", &fan->reg, + sizeof(cell_t)); + OF_getprop(child, "min-value", &fan->fan.min_rpm, + sizeof(int)); + OF_getprop(child, "max-value", &fan->fan.max_rpm, + sizeof(int)); + OF_getprop(child, "zone", &fan->fan.zone, + sizeof(int)); + + if (OF_getprop(child, "unmanaged-value", + &fan->fan.default_rpm, + sizeof(int)) != sizeof(int)) + fan->fan.default_rpm = fan->fan.max_rpm; + + OF_getprop(child, "location", fan->fan.name, + sizeof(fan->fan.name)); + + if (fan->type == SMU_FAN_RPM) + fan->setpoint = smu_fan_read_rpm(fan); + else + smu_fan_read_pwm(fan, &fan->setpoint, &fan->rpm); +} + +/* On the first call count the number of fans. In the second call, + * after allocating the fan struct, fill the properties of the fans. + */ +static int +smu_count_fans(device_t dev) +{ + struct smu_softc *sc; + phandle_t child, node, root; + int nfans = 0; + + node = ofw_bus_get_node(dev); + sc = device_get_softc(dev); + + /* First find the fanroots and count the number of fans. */ + for (root = OF_child(node); root != 0; root = OF_peer(root)) { + char name[32]; + memset(name, 0, sizeof(name)); + OF_getprop(root, "name", name, sizeof(name)); + if (strncmp(name, "rpm-fans", 9) == 0 || + strncmp(name, "pwm-fans", 9) == 0 || + strncmp(name, "fans", 5) == 0) + for (child = OF_child(root); child != 0; + child = OF_peer(child)) { + nfans++; + /* When allocated, fill the fan properties. */ + if (sc->sc_fans != NULL) + smu_fill_fan_prop(dev, child, + nfans - 1); + } + } + if (nfans == 0) { + device_printf(dev, "WARNING: No fans detected!\n"); + return (0); + } + return (nfans); } static void @@ -749,79 +963,92 @@ smu_attach_fans(device_t dev, phandle_t struct smu_softc *sc; struct sysctl_oid *oid, *fanroot_oid; struct sysctl_ctx_list *ctx; - phandle_t child; - char type[32], sysctl_name[32]; - int i; + char sysctl_name[32]; + int i, j; sc = device_get_softc(dev); - sc->sc_nfans = 0; - for (child = OF_child(fanroot); child != 0; child = OF_peer(child)) - sc->sc_nfans++; - - if (sc->sc_nfans == 0) { - device_printf(dev, "WARNING: No fans detected!\n"); + /* Get the number of fans. */ + sc->sc_nfans = smu_count_fans(dev); + if (sc->sc_nfans == 0) return; - } + /* Now we're able to allocate memory for the fans struct. */ sc->sc_fans = malloc(sc->sc_nfans * sizeof(struct smu_fan), M_SMU, M_WAITOK | M_ZERO); - fan = sc->sc_fans; - sc->sc_nfans = 0; + /* Now fill in the properties. */ + smu_count_fans(dev); + + /* Register fans with pmac_thermal */ + for (i = 0; i < sc->sc_nfans; i++) + pmac_thermal_fan_register(&sc->sc_fans[i].fan); ctx = device_get_sysctl_ctx(dev); fanroot_oid = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "fans", CTLFLAG_RD, 0, "SMU Fan Information"); - for (child = OF_child(fanroot); child != 0; child = OF_peer(child)) { - OF_getprop(child, "device_type", type, sizeof(type)); - if (strcmp(type, "fan-rpm-control") != 0) - continue; + /* Add sysctls */ + for (i = 0; i < sc->sc_nfans; i++) { + fan = &sc->sc_fans[i]; + for (j = 0; j < strlen(fan->fan.name); j++) { + sysctl_name[j] = tolower(fan->fan.name[j]); + if (isspace(sysctl_name[j])) + sysctl_name[j] = '_'; + } + sysctl_name[j] = 0; + if (fan->type == SMU_FAN_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, + "minrpm", CTLTYPE_INT | CTLFLAG_RD, + &fan->fan.min_rpm, sizeof(int), + "Minimum allowed RPM"); + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, + "maxrpm", CTLTYPE_INT | CTLFLAG_RD, + &fan->fan.max_rpm, sizeof(int), + "Maximum allowed RPM"); + SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, + "rpm",CTLTYPE_INT | CTLFLAG_RW | + CTLFLAG_MPSAFE, dev, i, + smu_fanrpm_sysctl, "I", "Fan RPM"); - fan->dev = dev; - fan->old_style = 0; - OF_getprop(child, "reg", &fan->reg, sizeof(cell_t)); - OF_getprop(child, "min-value", &fan->fan.min_rpm, sizeof(int)); - OF_getprop(child, "max-value", &fan->fan.max_rpm, sizeof(int)); - OF_getprop(child, "zone", &fan->fan.zone, sizeof(int)); - - if (OF_getprop(child, "unmanaged-value", &fan->fan.default_rpm, - sizeof(int)) != sizeof(int)) - fan->fan.default_rpm = fan->fan.max_rpm; + fan->fan.read = (int (*)(struct pmac_fan *))smu_fan_read_rpm; + fan->fan.set = (int (*)(struct pmac_fan *, int))smu_fan_set_rpm; - fan->setpoint = smu_fan_read_rpm(fan); + } else { + 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, + &fan->fan.min_rpm, sizeof(int), + "Minimum allowed PWM in %"); + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, + "maxpwm", CTLTYPE_INT | CTLFLAG_RD, + &fan->fan.max_rpm, sizeof(int), + "Maximum allowed PWM in %"); + SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, + "pwm",CTLTYPE_INT | CTLFLAG_RW | + CTLFLAG_MPSAFE, dev, + SMU_PWM_SYSCTL_PWM | i, + smu_fanrpm_sysctl, "I", "Fan PWM in %"); + SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, + "rpm",CTLTYPE_INT | CTLFLAG_RD | + CTLFLAG_MPSAFE, dev, + SMU_PWM_SYSCTL_RPM | i, + smu_fanrpm_sysctl, "I", "Fan RPM"); + fan->fan.read = NULL; + fan->fan.set = (int (*)(struct pmac_fan *, int))smu_fan_set_pwm; - OF_getprop(child, "location", fan->fan.name, - sizeof(fan->fan.name)); - - /* Add sysctls */ - for (i = 0; i < strlen(fan->fan.name); i++) { - sysctl_name[i] = tolower(fan->fan.name[i]); - if (isspace(sysctl_name[i])) - sysctl_name[i] = '_'; } - sysctl_name[i] = 0; - - 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, &fan->fan.min_rpm, sizeof(int), - "Minimum allowed RPM"); - SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "maxrpm", - CTLTYPE_INT | CTLFLAG_RD, &fan->fan.max_rpm, sizeof(int), - "Maximum allowed RPM"); - SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "rpm", - CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, dev, - sc->sc_nfans, smu_fanrpm_sysctl, "I", "Fan RPM"); - - fan->fan.read = (int (*)(struct pmac_fan *))smu_fan_read_rpm; - fan->fan.set = (int (*)(struct pmac_fan *, int))smu_fan_set_rpm; - pmac_thermal_fan_register(&fan->fan); - - fan++; - sc->sc_nfans++; + if (bootverbose) + device_printf(dev, "Fan: %s type: %d\n", + fan->fan.name, fan->type); } }