aboutsummaryrefslogtreecommitdiff
path: root/drivers/macintosh/windfarm_smu_controls.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/macintosh/windfarm_smu_controls.c')
-rw-r--r--drivers/macintosh/windfarm_smu_controls.c91
1 files changed, 68 insertions, 23 deletions
diff --git a/drivers/macintosh/windfarm_smu_controls.c b/drivers/macintosh/windfarm_smu_controls.c
index 4d811600bda..c155a54e863 100644
--- a/drivers/macintosh/windfarm_smu_controls.c
+++ b/drivers/macintosh/windfarm_smu_controls.c
@@ -18,13 +18,12 @@
#include <asm/prom.h>
#include <asm/machdep.h>
#include <asm/io.h>
-#include <asm/system.h>
#include <asm/sections.h>
#include <asm/smu.h>
#include "windfarm.h"
-#define VERSION "0.3"
+#define VERSION "0.4"
#undef DEBUG
@@ -34,6 +33,8 @@
#define DBG(args...) do { } while(0)
#endif
+static int smu_supports_new_fans_ops = 1;
+
/*
* SMU fans control object
*/
@@ -54,28 +55,54 @@ static int smu_set_fan(int pwm, u8 id, u16 value)
{
struct smu_cmd cmd;
u8 buffer[16];
- DECLARE_COMPLETION(comp);
+ DECLARE_COMPLETION_ONSTACK(comp);
int rc;
/* Fill SMU command structure */
cmd.cmd = SMU_CMD_FAN_COMMAND;
- cmd.data_len = 14;
+
+ /* The SMU has an "old" and a "new" way of setting the fan speed
+ * Unfortunately, I found no reliable way to know which one works
+ * on a given machine model. After some investigations it appears
+ * that MacOS X just tries the new one, and if it fails fallbacks
+ * to the old ones ... Ugh.
+ */
+ retry:
+ if (smu_supports_new_fans_ops) {
+ buffer[0] = 0x30;
+ buffer[1] = id;
+ *((u16 *)(&buffer[2])) = value;
+ cmd.data_len = 4;
+ } else {
+ if (id > 7)
+ return -EINVAL;
+ /* Fill argument buffer */
+ memset(buffer, 0, 16);
+ buffer[0] = pwm ? 0x10 : 0x00;
+ buffer[1] = 0x01 << id;
+ *((u16 *)&buffer[2 + id * 2]) = value;
+ cmd.data_len = 14;
+ }
+
cmd.reply_len = 16;
cmd.data_buf = cmd.reply_buf = buffer;
cmd.status = 0;
cmd.done = smu_done_complete;
cmd.misc = &comp;
- /* Fill argument buffer */
- memset(buffer, 0, 16);
- buffer[0] = pwm ? 0x10 : 0x00;
- buffer[1] = 0x01 << id;
- *((u16 *)&buffer[2 + id * 2]) = value;
-
rc = smu_queue_cmd(&cmd);
if (rc)
return rc;
wait_for_completion(&comp);
+
+ /* Handle fallback (see coment above) */
+ if (cmd.status != 0 && smu_supports_new_fans_ops) {
+ printk(KERN_WARNING "windfarm: SMU failed new fan command "
+ "falling back to old method\n");
+ smu_supports_new_fans_ops = 0;
+ goto retry;
+ }
+
return cmd.status;
}
@@ -131,14 +158,15 @@ static struct smu_fan_control *smu_fan_create(struct device_node *node,
int pwm_fan)
{
struct smu_fan_control *fct;
- s32 *v; u32 *reg;
- char *l;
+ const s32 *v;
+ const u32 *reg;
+ const char *l;
fct = kmalloc(sizeof(struct smu_fan_control), GFP_KERNEL);
if (fct == NULL)
return NULL;
fct->ctrl.ops = &smu_fan_ops;
- l = (char *)get_property(node, "location", NULL);
+ l = of_get_property(node, "location", NULL);
if (l == NULL)
goto fail;
@@ -158,19 +186,31 @@ static struct smu_fan_control *smu_fan_create(struct device_node *node,
/* Names used on desktop models */
if (!strcmp(l, "Rear Fan 0") || !strcmp(l, "Rear Fan") ||
- !strcmp(l, "Rear fan 0") || !strcmp(l, "Rear fan"))
+ !strcmp(l, "Rear fan 0") || !strcmp(l, "Rear fan") ||
+ !strcmp(l, "CPU A EXHAUST"))
fct->ctrl.name = "cpu-rear-fan-0";
- else if (!strcmp(l, "Rear Fan 1") || !strcmp(l, "Rear fan 1"))
+ else if (!strcmp(l, "Rear Fan 1") || !strcmp(l, "Rear fan 1") ||
+ !strcmp(l, "CPU B EXHAUST"))
fct->ctrl.name = "cpu-rear-fan-1";
else if (!strcmp(l, "Front Fan 0") || !strcmp(l, "Front Fan") ||
- !strcmp(l, "Front fan 0") || !strcmp(l, "Front fan"))
+ !strcmp(l, "Front fan 0") || !strcmp(l, "Front fan") ||
+ !strcmp(l, "CPU A INTAKE"))
fct->ctrl.name = "cpu-front-fan-0";
- else if (!strcmp(l, "Front Fan 1") || !strcmp(l, "Front fan 1"))
+ else if (!strcmp(l, "Front Fan 1") || !strcmp(l, "Front fan 1") ||
+ !strcmp(l, "CPU B INTAKE"))
fct->ctrl.name = "cpu-front-fan-1";
- else if (!strcmp(l, "Slots Fan") || !strcmp(l, "Slots fan"))
+ else if (!strcmp(l, "CPU A PUMP"))
+ fct->ctrl.name = "cpu-pump-0";
+ else if (!strcmp(l, "CPU B PUMP"))
+ fct->ctrl.name = "cpu-pump-1";
+ else if (!strcmp(l, "Slots Fan") || !strcmp(l, "Slots fan") ||
+ !strcmp(l, "EXPANSION SLOTS INTAKE"))
fct->ctrl.name = "slots-fan";
- else if (!strcmp(l, "Drive Bay") || !strcmp(l, "Drive bay"))
+ else if (!strcmp(l, "Drive Bay") || !strcmp(l, "Drive bay") ||
+ !strcmp(l, "DRIVE BAY A INTAKE"))
fct->ctrl.name = "drive-bay-fan";
+ else if (!strcmp(l, "BACKSIDE"))
+ fct->ctrl.name = "backside-fan";
/* Names used on iMac models */
if (!strcmp(l, "System Fan") || !strcmp(l, "System fan"))
@@ -179,23 +219,27 @@ static struct smu_fan_control *smu_fan_create(struct device_node *node,
fct->ctrl.name = "cpu-fan";
else if (!strcmp(l, "Hard Drive") || !strcmp(l, "Hard drive"))
fct->ctrl.name = "drive-bay-fan";
+ else if (!strcmp(l, "HDD Fan")) /* seen on iMac G5 iSight */
+ fct->ctrl.name = "hard-drive-fan";
+ else if (!strcmp(l, "ODD Fan")) /* same */
+ fct->ctrl.name = "optical-drive-fan";
/* Unrecognized fan, bail out */
if (fct->ctrl.name == NULL)
goto fail;
/* Get min & max values*/
- v = (s32 *)get_property(node, "min-value", NULL);
+ v = of_get_property(node, "min-value", NULL);
if (v == NULL)
goto fail;
fct->min = *v;
- v = (s32 *)get_property(node, "max-value", NULL);
+ v = of_get_property(node, "max-value", NULL);
if (v == NULL)
goto fail;
fct->max = *v;
/* Get "reg" value */
- reg = (u32 *)get_property(node, "reg", NULL);
+ reg = of_get_property(node, "reg", NULL);
if (reg == NULL)
goto fail;
fct->reg = *reg;
@@ -223,7 +267,8 @@ static int __init smu_controls_init(void)
/* Look for RPM fans */
for (fans = NULL; (fans = of_get_next_child(smu, fans)) != NULL;)
- if (!strcmp(fans->name, "rpm-fans"))
+ if (!strcmp(fans->name, "rpm-fans") ||
+ of_device_is_compatible(fans, "smu-rpm-fans"))
break;
for (fan = NULL;
fans && (fan = of_get_next_child(fans, fan)) != NULL;) {