Merge branch 'master'
[linux-2.6] / drivers / macintosh / windfarm_smu_controls.c
1 /*
2  * Windfarm PowerMac thermal control. SMU based controls
3  *
4  * (c) Copyright 2005 Benjamin Herrenschmidt, IBM Corp.
5  *                    <benh@kernel.crashing.org>
6  *
7  * Released under the term of the GNU GPL v2.
8  */
9
10 #include <linux/types.h>
11 #include <linux/errno.h>
12 #include <linux/kernel.h>
13 #include <linux/delay.h>
14 #include <linux/slab.h>
15 #include <linux/init.h>
16 #include <linux/wait.h>
17 #include <asm/prom.h>
18 #include <asm/machdep.h>
19 #include <asm/io.h>
20 #include <asm/system.h>
21 #include <asm/sections.h>
22 #include <asm/smu.h>
23
24 #include "windfarm.h"
25
26 #define VERSION "0.3"
27
28 #undef DEBUG
29
30 #ifdef DEBUG
31 #define DBG(args...)    printk(args)
32 #else
33 #define DBG(args...)    do { } while(0)
34 #endif
35
36 /*
37  * SMU fans control object
38  */
39
40 static LIST_HEAD(smu_fans);
41
42 struct smu_fan_control {
43         struct list_head        link;
44         int                     fan_type;       /* 0 = rpm, 1 = pwm */
45         u32                     reg;            /* index in SMU */
46         s32                     value;          /* current value */
47         s32                     min, max;       /* min/max values */
48         struct wf_control       ctrl;
49 };
50 #define to_smu_fan(c) container_of(c, struct smu_fan_control, ctrl)
51
52 static int smu_set_fan(int pwm, u8 id, u16 value)
53 {
54         struct smu_cmd cmd;
55         u8 buffer[16];
56         DECLARE_COMPLETION(comp);
57         int rc;
58
59         /* Fill SMU command structure */
60         cmd.cmd = SMU_CMD_FAN_COMMAND;
61         cmd.data_len = 14;
62         cmd.reply_len = 16;
63         cmd.data_buf = cmd.reply_buf = buffer;
64         cmd.status = 0;
65         cmd.done = smu_done_complete;
66         cmd.misc = &comp;
67
68         /* Fill argument buffer */
69         memset(buffer, 0, 16);
70         buffer[0] = pwm ? 0x10 : 0x00;
71         buffer[1] = 0x01 << id;
72         *((u16 *)&buffer[2 + id * 2]) = value;
73
74         rc = smu_queue_cmd(&cmd);
75         if (rc)
76                 return rc;
77         wait_for_completion(&comp);
78         return cmd.status;
79 }
80
81 static void smu_fan_release(struct wf_control *ct)
82 {
83         struct smu_fan_control *fct = to_smu_fan(ct);
84
85         kfree(fct);
86 }
87
88 static int smu_fan_set(struct wf_control *ct, s32 value)
89 {
90         struct smu_fan_control *fct = to_smu_fan(ct);
91
92         if (value < fct->min)
93                 value = fct->min;
94         if (value > fct->max)
95                 value = fct->max;
96         fct->value = value;
97
98         return smu_set_fan(fct->fan_type, fct->reg, value);
99 }
100
101 static int smu_fan_get(struct wf_control *ct, s32 *value)
102 {
103         struct smu_fan_control *fct = to_smu_fan(ct);
104         *value = fct->value; /* todo: read from SMU */
105         return 0;
106 }
107
108 static s32 smu_fan_min(struct wf_control *ct)
109 {
110         struct smu_fan_control *fct = to_smu_fan(ct);
111         return fct->min;
112 }
113
114 static s32 smu_fan_max(struct wf_control *ct)
115 {
116         struct smu_fan_control *fct = to_smu_fan(ct);
117         return fct->max;
118 }
119
120 static struct wf_control_ops smu_fan_ops = {
121         .set_value      = smu_fan_set,
122         .get_value      = smu_fan_get,
123         .get_min        = smu_fan_min,
124         .get_max        = smu_fan_max,
125         .release        = smu_fan_release,
126         .owner          = THIS_MODULE,
127 };
128
129 static struct smu_fan_control *smu_fan_create(struct device_node *node,
130                                               int pwm_fan)
131 {
132         struct smu_fan_control *fct;
133         s32 *v; u32 *reg;
134         char *l;
135
136         fct = kmalloc(sizeof(struct smu_fan_control), GFP_KERNEL);
137         if (fct == NULL)
138                 return NULL;
139         fct->ctrl.ops = &smu_fan_ops;
140         l = (char *)get_property(node, "location", NULL);
141         if (l == NULL)
142                 goto fail;
143
144         fct->fan_type = pwm_fan;
145         fct->ctrl.type = pwm_fan ? WF_CONTROL_PWM_FAN : WF_CONTROL_RPM_FAN;
146
147         /* We use the name & location here the same way we do for SMU sensors,
148          * see the comment in windfarm_smu_sensors.c. The locations are a bit
149          * less consistent here between the iMac and the desktop models, but
150          * that is good enough for our needs for now at least.
151          *
152          * One problem though is that Apple seem to be inconsistent with case
153          * and the kernel doesn't have strcasecmp =P
154          */
155
156         fct->ctrl.name = NULL;
157
158         /* Names used on desktop models */
159         if (!strcmp(l, "Rear Fan 0") || !strcmp(l, "Rear Fan") ||
160             !strcmp(l, "Rear fan 0") || !strcmp(l, "Rear fan"))
161                 fct->ctrl.name = "cpu-rear-fan-0";
162         else if (!strcmp(l, "Rear Fan 1") || !strcmp(l, "Rear fan 1"))
163                 fct->ctrl.name = "cpu-rear-fan-1";
164         else if (!strcmp(l, "Front Fan 0") || !strcmp(l, "Front Fan") ||
165                  !strcmp(l, "Front fan 0") || !strcmp(l, "Front fan"))
166                 fct->ctrl.name = "cpu-front-fan-0";
167         else if (!strcmp(l, "Front Fan 1") || !strcmp(l, "Front fan 1"))
168                 fct->ctrl.name = "cpu-front-fan-1";
169         else if (!strcmp(l, "Slots Fan") || !strcmp(l, "Slots fan"))
170                 fct->ctrl.name = "slots-fan";
171         else if (!strcmp(l, "Drive Bay") || !strcmp(l, "Drive bay"))
172                 fct->ctrl.name = "drive-bay-fan";
173
174         /* Names used on iMac models */
175         if (!strcmp(l, "System Fan") || !strcmp(l, "System fan"))
176                 fct->ctrl.name = "system-fan";
177         else if (!strcmp(l, "CPU Fan") || !strcmp(l, "CPU fan"))
178                 fct->ctrl.name = "cpu-fan";
179         else if (!strcmp(l, "Hard Drive") || !strcmp(l, "Hard drive"))
180                 fct->ctrl.name = "drive-bay-fan";
181
182         /* Unrecognized fan, bail out */
183         if (fct->ctrl.name == NULL)
184                 goto fail;
185
186         /* Get min & max values*/
187         v = (s32 *)get_property(node, "min-value", NULL);
188         if (v == NULL)
189                 goto fail;
190         fct->min = *v;
191         v = (s32 *)get_property(node, "max-value", NULL);
192         if (v == NULL)
193                 goto fail;
194         fct->max = *v;
195
196         /* Get "reg" value */
197         reg = (u32 *)get_property(node, "reg", NULL);
198         if (reg == NULL)
199                 goto fail;
200         fct->reg = *reg;
201
202         if (wf_register_control(&fct->ctrl))
203                 goto fail;
204
205         return fct;
206  fail:
207         kfree(fct);
208         return NULL;
209 }
210
211
212 static int __init smu_controls_init(void)
213 {
214         struct device_node *smu, *fans, *fan;
215
216         if (!smu_present())
217                 return -ENODEV;
218
219         smu = of_find_node_by_type(NULL, "smu");
220         if (smu == NULL)
221                 return -ENODEV;
222
223         /* Look for RPM fans */
224         for (fans = NULL; (fans = of_get_next_child(smu, fans)) != NULL;)
225                 if (!strcmp(fans->name, "rpm-fans"))
226                         break;
227         for (fan = NULL;
228              fans && (fan = of_get_next_child(fans, fan)) != NULL;) {
229                 struct smu_fan_control *fct;
230
231                 fct = smu_fan_create(fan, 0);
232                 if (fct == NULL) {
233                         printk(KERN_WARNING "windfarm: Failed to create SMU "
234                                "RPM fan %s\n", fan->name);
235                         continue;
236                 }
237                 list_add(&fct->link, &smu_fans);
238         }
239         of_node_put(fans);
240
241
242         /* Look for PWM fans */
243         for (fans = NULL; (fans = of_get_next_child(smu, fans)) != NULL;)
244                 if (!strcmp(fans->name, "pwm-fans"))
245                         break;
246         for (fan = NULL;
247              fans && (fan = of_get_next_child(fans, fan)) != NULL;) {
248                 struct smu_fan_control *fct;
249
250                 fct = smu_fan_create(fan, 1);
251                 if (fct == NULL) {
252                         printk(KERN_WARNING "windfarm: Failed to create SMU "
253                                "PWM fan %s\n", fan->name);
254                         continue;
255                 }
256                 list_add(&fct->link, &smu_fans);
257         }
258         of_node_put(fans);
259         of_node_put(smu);
260
261         return 0;
262 }
263
264 static void __exit smu_controls_exit(void)
265 {
266         struct smu_fan_control *fct;
267
268         while (!list_empty(&smu_fans)) {
269                 fct = list_entry(smu_fans.next, struct smu_fan_control, link);
270                 list_del(&fct->link);
271                 wf_unregister_control(&fct->ctrl);
272         }
273 }
274
275
276 module_init(smu_controls_init);
277 module_exit(smu_controls_exit);
278
279 MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
280 MODULE_DESCRIPTION("SMU control objects for PowerMacs thermal control");
281 MODULE_LICENSE("GPL");
282