diff options
Diffstat (limited to 'drivers/macintosh/windfarm_smu_controls.c')
-rw-r--r-- | drivers/macintosh/windfarm_smu_controls.c | 282 |
1 files changed, 282 insertions, 0 deletions
diff --git a/drivers/macintosh/windfarm_smu_controls.c b/drivers/macintosh/windfarm_smu_controls.c new file mode 100644 index 000000000000..2c3158c81ff2 --- /dev/null +++ b/drivers/macintosh/windfarm_smu_controls.c | |||
@@ -0,0 +1,282 @@ | |||
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 = ∁ | ||
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 | |||