apple_smc_temp.c revision 1.5 1 /* $NetBSD: apple_smc_temp.c,v 1.5 2015/04/23 23:23:00 pgoyette Exp $ */
2
3 /*
4 * Apple System Management Controller: Temperature Sensors
5 */
6
7 /*-
8 * Copyright (c) 2013 The NetBSD Foundation, Inc.
9 * All rights reserved.
10 *
11 * This code is derived from software contributed to The NetBSD Foundation
12 * by Taylor R. Campbell.
13 *
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions
16 * are met:
17 * 1. Redistributions of source code must retain the above copyright
18 * notice, this list of conditions and the following disclaimer.
19 * 2. Redistributions in binary form must reproduce the above copyright
20 * notice, this list of conditions and the following disclaimer in the
21 * documentation and/or other materials provided with the distribution.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
24 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
25 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
27 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33 * POSSIBILITY OF SUCH DAMAGE.
34 */
35
36 #include <sys/cdefs.h>
37 __KERNEL_RCSID(0, "$NetBSD: apple_smc_temp.c,v 1.5 2015/04/23 23:23:00 pgoyette Exp $");
38
39 #include <sys/types.h>
40 #include <sys/param.h>
41 #include <sys/device.h>
42 #include <sys/kmem.h>
43 #include <sys/module.h>
44 #include <sys/systm.h>
45
46 #include <dev/ic/apple_smc.h>
47
48 #include <dev/sysmon/sysmonvar.h>
49
50 struct apple_smc_temp_softc {
51 device_t sc_dev;
52 struct apple_smc_tag *sc_smc;
53 struct sysmon_envsys *sc_sme;
54 struct {
55 struct apple_smc_key *sensor_key;
56 struct envsys_data sensor_data;
57 } *sc_sensors;
58 size_t sc_nsensors;
59 };
60
61 static int apple_smc_temp_match(device_t, cfdata_t, void *);
62 static void apple_smc_temp_attach(device_t, device_t, void *);
63 static int apple_smc_temp_detach(device_t, int);
64 static void apple_smc_temp_refresh(struct sysmon_envsys *,
65 struct envsys_data *);
66 static int apple_smc_temp_count_sensors(struct apple_smc_tag *,
67 uint32_t *);
68 static void apple_smc_temp_count_sensors_scanner(struct apple_smc_tag *,
69 void *, struct apple_smc_key *);
70 static int apple_smc_temp_find_sensors(struct apple_smc_temp_softc *);
71 static int apple_smc_temp_find_sensors_init(struct apple_smc_tag *,
72 void *, uint32_t);
73 static void apple_smc_temp_find_sensors_scanner(struct apple_smc_tag *,
74 void *, struct apple_smc_key *);
75 static void apple_smc_temp_release_keys(struct apple_smc_temp_softc *);
76 static int apple_smc_scan_temp_sensors(struct apple_smc_tag *, void *,
77 int (*)(struct apple_smc_tag *, void *, uint32_t),
78 void (*)(struct apple_smc_tag *, void *,
79 struct apple_smc_key *));
80 static int apple_smc_bound_temp_sensors(struct apple_smc_tag *,
81 uint32_t *, uint32_t *);
82 static bool apple_smc_temp_sensor_p(const struct apple_smc_key *);
83
84 CFATTACH_DECL_NEW(apple_smc_temp, sizeof(struct apple_smc_temp_softc),
85 apple_smc_temp_match, apple_smc_temp_attach, apple_smc_temp_detach, NULL);
86
87 static int
88 apple_smc_temp_match(device_t parent, cfdata_t match, void *aux)
89 {
90 const struct apple_smc_attach_args *const asa = aux;
91 uint32_t nsensors;
92 int error;
93
94 /* Find how many temperature sensors we have. */
95 error = apple_smc_temp_count_sensors(asa->asa_smc, &nsensors);
96 if (error)
97 return 0;
98
99 /* If there aren't any, don't bother attaching. */
100 if (nsensors == 0)
101 return 0;
102
103 return 1;
104 }
105
106 static void
107 apple_smc_temp_attach(device_t parent, device_t self, void *aux)
108 {
109 struct apple_smc_temp_softc *const sc = device_private(self);
110 const struct apple_smc_attach_args *const asa = aux;
111 int error;
112
113 /* Identify ourselves. */
114 aprint_normal(": Apple SMC temperature sensors\n");
115
116 /* Initialize the softc. */
117 sc->sc_dev = self;
118 sc->sc_smc = asa->asa_smc;
119
120 /* Create a sysmon_envsys record, but don't register it yet. */
121 sc->sc_sme = sysmon_envsys_create();
122 sc->sc_sme->sme_name = device_xname(self);
123 sc->sc_sme->sme_cookie = sc;
124 sc->sc_sme->sme_refresh = apple_smc_temp_refresh;
125
126 /* Find and attach all the sensors. */
127 error = apple_smc_temp_find_sensors(sc);
128 if (error) {
129 aprint_error_dev(self, "failed to find sensors: %d\n", error);
130 goto fail;
131 }
132
133 /* Sensors are all attached. Register with sysmon_envsys now. */
134 error = sysmon_envsys_register(sc->sc_sme);
135 if (error) {
136 aprint_error_dev(self, "failed to register with sysmon_envsys:"
137 " %d\n", error);
138 goto fail;
139 }
140
141 /* Success! */
142 return;
143
144 fail: sysmon_envsys_destroy(sc->sc_sme);
145 sc->sc_sme = NULL;
146 }
147
148 static int
149 apple_smc_temp_detach(device_t self, int flags)
150 {
151 struct apple_smc_temp_softc *const sc = device_private(self);
152
153 /* If we registered with sysmon_envsys, unregister. */
154 if (sc->sc_sme != NULL) {
155 sysmon_envsys_unregister(sc->sc_sme);
156 sc->sc_sme = NULL;
157
158 KASSERT(sc->sc_sensors != NULL);
159 KASSERT(sc->sc_nsensors > 0);
160
161 /* Release the keys and free the memory for sensor records. */
162 apple_smc_temp_release_keys(sc);
163 kmem_free(sc->sc_sensors,
164 (sizeof(sc->sc_sensors[0]) * sc->sc_nsensors));
165 sc->sc_sensors = NULL;
166 sc->sc_nsensors = 0;
167 }
168
169 /* Success! */
170 return 0;
171 }
172
173 static void
174 apple_smc_temp_refresh(struct sysmon_envsys *sme, struct envsys_data *edata)
175 {
176 struct apple_smc_temp_softc *const sc = sme->sme_cookie;
177 const struct apple_smc_key *key;
178 uint16_t utemp16;
179 int32_t temp;
180 int error;
181
182 /* Sanity-check the sensor number out of paranoia. */
183 if (edata->sensor >= sc->sc_nsensors) {
184 aprint_error_dev(sc->sc_dev, "unknown sensor %"PRIu32"\n",
185 edata->sensor);
186 return;
187 }
188
189 /* Read the raw temperature sensor value. */
190 key = sc->sc_sensors[edata->sensor].sensor_key;
191 KASSERT(key != NULL);
192 error = apple_smc_read_key_2(sc->sc_smc, key, &utemp16);
193 if (error) {
194 aprint_error_dev(sc->sc_dev,
195 "failed to read temperature sensor %"PRIu32" (%s): %d\n",
196 edata->sensor, apple_smc_key_name(key), error);
197 edata->state = ENVSYS_SINVALID;
198 return;
199 }
200
201 /* Sign-extend, in case we ever get below freezing... */
202 temp = (int16_t)utemp16;
203
204 /* Convert to `millicentigrade'. */
205 temp *= 250;
206 temp >>= 6;
207
208 /* Convert to millikelvins. */
209 temp += 273150;
210
211 /* Finally, convert to microkelvins as sysmon_envsys wants. */
212 temp *= 1000;
213
214 /* Success! */
215 edata->value_cur = temp;
216 edata->state = ENVSYS_SVALID;
217 }
218
219 static int
220 apple_smc_temp_count_sensors(struct apple_smc_tag *smc, uint32_t *nsensors)
221 {
222
223 /* Start with zero sensors. */
224 *nsensors = 0;
225
226 /* Count 'em. */
227 return apple_smc_scan_temp_sensors(smc, nsensors,
228 NULL,
229 &apple_smc_temp_count_sensors_scanner);
230 }
231
232 static void
233 apple_smc_temp_count_sensors_scanner(struct apple_smc_tag *smc, void *arg,
234 struct apple_smc_key *key)
235 {
236 uint32_t *const nsensors = arg;
237
238 (*nsensors)++;
239 apple_smc_release_key(smc, key);
240 }
241
242 struct fss { /* Find Sensors State */
243 struct apple_smc_temp_softc *fss_sc;
244 unsigned int fss_sensor;
245 };
246
247 static int
248 apple_smc_temp_find_sensors(struct apple_smc_temp_softc *sc)
249 {
250 struct fss fss;
251 int error;
252
253 /* Start with zero sensors. */
254 fss.fss_sc = sc;
255 fss.fss_sensor = 0;
256
257 /* Find 'em. */
258 error = apple_smc_scan_temp_sensors(sc->sc_smc, &fss,
259 &apple_smc_temp_find_sensors_init,
260 &apple_smc_temp_find_sensors_scanner);
261 if (error)
262 return error;
263
264 /*
265 * Success guarantees that sc->sc_nsensors will be nonzero and
266 * sc->sc_sensors will be allocated.
267 */
268 KASSERT(sc->sc_sensors != NULL);
269 KASSERT(sc->sc_nsensors > 0);
270
271 /* If we didn't find any sensors, bail. */
272 if (fss.fss_sensor == 0) {
273 kmem_free(sc->sc_sensors, sc->sc_nsensors);
274 sc->sc_sensors = NULL;
275 sc->sc_nsensors = 0;
276 return EIO;
277 }
278
279 /* Shrink the array if we overshot. */
280 if (fss.fss_sensor < sc->sc_nsensors) {
281 void *const sensors = kmem_alloc((fss.fss_sensor *
282 sizeof(sc->sc_sensors[0])), KM_SLEEP);
283
284 (void)memcpy(sensors, sc->sc_sensors,
285 (fss.fss_sensor * sizeof(sc->sc_sensors[0])));
286 kmem_free(sc->sc_sensors, sc->sc_nsensors);
287 sc->sc_sensors = sensors;
288 sc->sc_nsensors = fss.fss_sensor;
289 }
290
291 /* Success! */
292 return 0;
293 }
294
295 static int
296 apple_smc_temp_find_sensors_init(struct apple_smc_tag *smc, void *arg,
297 uint32_t nsensors)
298 {
299 struct fss *const fss = arg;
300
301 /* Record the maximum number of sensors we may have. */
302 fss->fss_sc->sc_nsensors = nsensors;
303
304 /* If we found a maximum of zero sensors, bail. */
305 if (nsensors == 0) {
306 fss->fss_sc->sc_sensors = NULL;
307 return EIO;
308 }
309
310 /*
311 * If there may be any sensors, optimistically allocate as many
312 * records for them as we may possibly need.
313 */
314 fss->fss_sc->sc_sensors = kmem_alloc((nsensors *
315 sizeof(fss->fss_sc->sc_sensors[0])), KM_SLEEP);
316
317 /* Success! */
318 return 0;
319 }
320
321 static void
322 apple_smc_temp_find_sensors_scanner(struct apple_smc_tag *smc, void *arg,
323 struct apple_smc_key *key)
324 {
325 struct fss *const fss = arg;
326 const uint32_t sensor = fss->fss_sensor;
327 struct envsys_data *const edata =
328 &fss->fss_sc->sc_sensors[sensor].sensor_data;
329 int error;
330
331 /* Initialize the envsys_data record for this temperature sensor. */
332 edata->units = ENVSYS_STEMP;
333 edata->state = ENVSYS_SINVALID;
334 edata->flags = ENVSYS_FHAS_ENTROPY;
335
336 /*
337 * Use the SMC key name as the temperature sensor's name.
338 *
339 * XXX We ought to use a more meaningful name based on a table
340 * of known temperature sensors.
341 */
342 CTASSERT(sizeof(edata->desc) >= 4);
343 (void)strlcpy(edata->desc, apple_smc_key_name(key), 4);
344
345 /* Attach this temperature sensor to sysmon_envsys. */
346 error = sysmon_envsys_sensor_attach(fss->fss_sc->sc_sme, edata);
347 if (error) {
348 aprint_error_dev(fss->fss_sc->sc_dev,
349 "failed to attach temperature sensor %s: %d\n",
350 apple_smc_key_name(key), error);
351 return;
352 }
353
354 /* Success! */
355 fss->fss_sc->sc_sensors[sensor].sensor_key = key;
356 fss->fss_sensor++;
357 }
358
359 static void
360 apple_smc_temp_release_keys(struct apple_smc_temp_softc *sc)
361 {
362 uint32_t sensor;
363
364 for (sensor = 0; sensor < sc->sc_nsensors; sensor++) {
365 KASSERT(sc->sc_sensors[sensor].sensor_key != NULL);
366 apple_smc_release_key(sc->sc_smc,
367 sc->sc_sensors[sensor].sensor_key);
368 }
369 }
370
371 static int
372 apple_smc_scan_temp_sensors(struct apple_smc_tag *smc, void *arg,
373 int (*init)(struct apple_smc_tag *, void *, uint32_t),
374 void (*scanner)(struct apple_smc_tag *, void *, struct apple_smc_key *))
375 {
376 uint32_t tstart, ustart, i;
377 struct apple_smc_key *key;
378 int error;
379
380 /* Find [start, end) bounds on the temperature sensor key indices. */
381 error = apple_smc_bound_temp_sensors(smc, &tstart, &ustart);
382 if (error)
383 return error;
384 KASSERT(tstart <= ustart);
385
386 /* Inform the caller of the number of candidates. */
387 if (init != NULL) {
388 error = (*init)(smc, arg, (ustart - tstart));
389 if (error)
390 return error;
391 }
392
393 /* Take a closer look at all the candidates. */
394 for (i = tstart; i < ustart; i++) {
395 error = apple_smc_nth_key(smc, i, NULL, &key);
396 if (error)
397 continue;
398
399 /* Skip it if it's not a temperature sensor. */
400 if (!apple_smc_temp_sensor_p(key)) {
401 apple_smc_release_key(smc, key);
402 continue;
403 }
404
405 /* Scan it if it is one. */
406 (*scanner)(smc, arg, key);
407 }
408
409 /* Success! */
410 return 0;
411 }
412
413 static bool
414 apple_smc_temp_sensor_p(const struct apple_smc_key *key)
415 {
416
417 /* It's a temperature sensor iff its type is sp78. */
418 return (0 == memcmp(apple_smc_key_desc(key)->asd_type,
419 APPLE_SMC_TYPE_SP78, 4));
420 }
421
422 static int
423 apple_smc_bound_temp_sensors(struct apple_smc_tag *smc, uint32_t *tstart,
424 uint32_t *ustart)
425 {
426 int error;
427
428 /* Find the first `T...' key. */
429 error = apple_smc_key_search(smc, "T", tstart);
430 if (error)
431 return error;
432
433 /* Find the first `U...' key. */
434 error = apple_smc_key_search(smc, "U", ustart);
435 if (error)
436 return error;
437
438 /* Sanity check: `T...' keys had better precede `U...' keys. */
439 if (!(*tstart <= *ustart))
440 return EIO;
441
442 /* Success! */
443 return 0;
444 }
445
446 MODULE(MODULE_CLASS_DRIVER, apple_smc_temp, "apple_smc,sysmon_envsys");
447
448 #ifdef _MODULE
449 #include "ioconf.c"
450 #endif
451
452 static int
453 apple_smc_temp_modcmd(modcmd_t cmd, void *arg __unused)
454 {
455 #ifdef _MODULE
456 int error;
457 #endif
458
459 switch (cmd) {
460 case MODULE_CMD_INIT:
461 #ifdef _MODULE
462 error = config_init_component(cfdriver_ioconf_apple_smc_temp,
463 cfattach_ioconf_apple_smc_temp,
464 cfdata_ioconf_apple_smc_temp);
465 if (error)
466 return error;
467 #endif
468 return 0;
469
470 case MODULE_CMD_FINI:
471 #ifdef _MODULE
472 error = config_fini_component(cfdriver_ioconf_apple_smc_temp,
473 cfattach_ioconf_apple_smc_temp,
474 cfdata_ioconf_apple_smc_temp);
475 if (error)
476 return error;
477 #endif
478 return 0;
479
480 default:
481 return ENOTTY;
482 }
483 }
484