Annotation of sys/arch/macppc/dev/smu.c, Revision 1.1.1.1
1.1 nbrk 1: /* $OpenBSD: smu.c,v 1.19 2007/05/20 23:38:52 thib Exp $ */
2:
3: /*
4: * Copyright (c) 2005 Mark Kettenis
5: *
6: * Permission to use, copy, modify, and distribute this software for any
7: * purpose with or without fee is hereby granted, provided that the above
8: * copyright notice and this permission notice appear in all copies.
9: *
10: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17: */
18:
19: #include <sys/param.h>
20: #include <sys/systm.h>
21: #include <sys/device.h>
22: #include <sys/kernel.h>
23: #include <sys/rwlock.h>
24: #include <sys/proc.h>
25: #include <sys/sensors.h>
26:
27: #include <machine/autoconf.h>
28: #include <machine/cpu.h>
29:
30: #include <dev/clock_subr.h>
31: #include <dev/i2c/i2cvar.h>
32: #include <dev/ofw/openfirm.h>
33:
34: #include <arch/macppc/dev/maci2cvar.h>
35:
36: int smu_match(struct device *, void *, void *);
37: void smu_attach(struct device *, struct device *, void *);
38:
39: #define SMU_MAXFANS 3
40:
41: struct smu_fan {
42: u_int8_t reg;
43: u_int16_t min_rpm;
44: u_int16_t max_rpm;
45: u_int16_t unmanaged_rpm;
46: struct ksensor sensor;
47: };
48:
49: #define SMU_MAXSENSORS 3
50:
51: struct smu_sensor {
52: u_int8_t reg;
53: struct ksensor sensor;
54: };
55:
56: struct smu_softc {
57: struct device sc_dev;
58:
59: /* SMU command buffer. */
60: bus_dma_tag_t sc_dmat;
61: bus_dmamap_t sc_cmdmap;
62: bus_dma_segment_t sc_cmdseg[1];
63: caddr_t sc_cmd;
64: struct rwlock sc_lock;
65:
66: /* Doorbell and mailbox. */
67: struct ppc_bus_space sc_mem_bus_space;
68: bus_space_tag_t sc_memt;
69: bus_space_handle_t sc_gpioh;
70: bus_space_handle_t sc_buffh;
71:
72: struct smu_fan sc_fans[SMU_MAXFANS];
73: int sc_num_fans;
74:
75: struct smu_sensor sc_sensors[SMU_MAXSENSORS];
76: int sc_num_sensors;
77:
78: struct ksensordev sc_sensordev;
79:
80: u_int16_t sc_cpu_diode_scale;
81: int16_t sc_cpu_diode_offset;
82: u_int16_t sc_cpu_volt_scale;
83: int16_t sc_cpu_volt_offset;
84: u_int16_t sc_cpu_curr_scale;
85: int16_t sc_cpu_curr_offset;
86:
87: struct i2c_controller sc_i2c_tag;
88: };
89:
90: struct cfattach smu_ca = {
91: sizeof(struct smu_softc), smu_match, smu_attach
92: };
93:
94: struct cfdriver smu_cd = {
95: NULL, "smu", DV_DULL,
96: };
97:
98: /* SMU command */
99: struct smu_cmd {
100: u_int8_t cmd;
101: u_int8_t len;
102: u_int8_t data[254];
103: };
104: #define SMU_CMDSZ sizeof(struct smu_cmd)
105:
106: /* RTC */
107: #define SMU_RTC 0x8e
108: #define SMU_RTC_SET_DATETIME 0x80
109: #define SMU_RTC_GET_DATETIME 0x81
110:
111: /* ADC */
112: #define SMU_ADC 0xd8
113:
114: /* Fan control */
115: #define SMU_FAN 0x4a
116:
117: /* Data partitions */
118: #define SMU_PARTITION 0x3e
119: #define SMU_PARTITION_LATEST 0x01
120: #define SMU_PARTITION_BASE 0x02
121: #define SMU_PARTITION_UPDATE 0x03
122:
123: /* I2C */
124: #define SMU_I2C 0x9a
125: #define SMU_I2C_SIMPLE 0x00
126: #define SMU_I2C_NORMAL 0x01
127: #define SMU_I2C_COMBINED 0x02
128:
129: /* Power Management */
130: #define SMU_POWER 0xaa
131:
132: /* Miscellaneous */
133: #define SMU_MISC 0xee
134: #define SMU_MISC_GET_DATA 0x02
135:
136: int smu_intr(void *);
137:
138: int smu_do_cmd(struct smu_softc *, int);
139: int smu_time_read(time_t *);
140: int smu_time_write(time_t);
141: int smu_get_datablock(struct smu_softc *sc, u_int8_t, u_int8_t *, size_t);
142: int smu_fan_set_rpm(struct smu_softc *, struct smu_fan *, u_int16_t);
143: int smu_fan_refresh(struct smu_softc *, struct smu_fan *);
144: int smu_sensor_refresh(struct smu_softc *, struct smu_sensor *);
145: void smu_refresh_sensors(void *);
146:
147: int smu_i2c_acquire_bus(void *, int);
148: void smu_i2c_release_bus(void *, int);
149: int smu_i2c_exec(void *, i2c_op_t, i2c_addr_t,
150: const void *, size_t, void *buf, size_t, int);
151:
152: void smu_slew_voltage(u_int);
153:
154: #define GPIO_DDR 0x04 /* Data direction */
155: #define GPIO_DDR_OUTPUT 0x04 /* Output */
156: #define GPIO_DDR_INPUT 0x00 /* Input */
157:
158: #define GPIO_LEVEL 0x02 /* Pin level (RO) */
159:
160: #define GPIO_DATA 0x01 /* Data */
161:
162: int
163: smu_match(struct device *parent, void *cf, void *aux)
164: {
165: struct confargs *ca = aux;
166:
167: if (strcmp(ca->ca_name, "smu") == 0)
168: return (1);
169: return (0);
170: }
171:
172: /* XXX */
173: extern struct powerpc_bus_dma_tag pci_bus_dma_tag;
174:
175: void
176: smu_attach(struct device *parent, struct device *self, void *aux)
177: {
178: struct smu_softc *sc = (struct smu_softc *)self;
179: struct confargs *ca = aux;
180: struct i2cbus_attach_args iba;
181: struct smu_fan *fan;
182: struct smu_sensor *sensor;
183: int nseg, node;
184: char type[32], loc[32];
185: u_int32_t reg, intr, gpio, val;
186: u_int8_t data[12];
187:
188: /* XXX */
189: sc->sc_mem_bus_space.bus_base = 0x80000000;
190: sc->sc_mem_bus_space.bus_size = 0;
191: sc->sc_mem_bus_space.bus_io = 0;
192: sc->sc_memt = &sc->sc_mem_bus_space;
193:
194: /* Map smu-doorbell gpio. */
195: if (OF_getprop(ca->ca_node, "platform-doorbell-ack",
196: &node, sizeof node) <= 0 ||
197: OF_getprop(node, "reg", ®, sizeof reg) <= 0 ||
198: OF_getprop(node, "interrupts", &intr, sizeof intr) <= 0 ||
199: OF_getprop(OF_parent(node), "reg", &gpio, sizeof gpio) <= 0) {
200: printf(": cannot find smu-doorbell gpio\n");
201: return;
202: }
203: if (bus_space_map(sc->sc_memt, gpio + reg, 1, 0, &sc->sc_gpioh)) {
204: printf(": cannot map smu-doorbell gpio\n");
205: return;
206: }
207:
208: /* XXX Should get this from OF. */
209: if (bus_space_map(sc->sc_memt, 0x860c, 4, 0, &sc->sc_buffh)) {
210: printf(": cannot map smu-doorbell buffer\n");
211: return;
212: }
213:
214: /* XXX */
215: sc->sc_dmat = &pci_bus_dma_tag;
216:
217: /* Allocate and map SMU command buffer. */
218: if (bus_dmamem_alloc(sc->sc_dmat, SMU_CMDSZ, 0, 0,
219: sc->sc_cmdseg, 1, &nseg, BUS_DMA_NOWAIT)) {
220: printf(": cannot allocate cmd buffer\n");
221: return;
222: }
223: if (bus_dmamem_map(sc->sc_dmat, sc->sc_cmdseg, nseg,
224: SMU_CMDSZ, &sc->sc_cmd, BUS_DMA_NOWAIT)) {
225: printf(": cannot map cmd buffer\n");
226: bus_dmamem_free(sc->sc_dmat, sc->sc_cmdseg, 1);
227: return;
228: }
229: if (bus_dmamap_create(sc->sc_dmat, SMU_CMDSZ, 1, SMU_CMDSZ, 0,
230: BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &sc->sc_cmdmap)) {
231: printf(": cannot create cmd dmamap\n");
232: bus_dmamem_unmap(sc->sc_dmat, sc->sc_cmd, SMU_CMDSZ);
233: bus_dmamem_free(sc->sc_dmat, sc->sc_cmdseg, 1);
234: return;
235: }
236: if (bus_dmamap_load(sc->sc_dmat, sc->sc_cmdmap, sc->sc_cmd,
237: SMU_CMDSZ, NULL, BUS_DMA_NOWAIT)) {
238: printf(": cannot load cmd dmamap\n");
239: bus_dmamap_destroy(sc->sc_dmat, sc->sc_cmdmap);
240: bus_dmamem_unmap(sc->sc_dmat, sc->sc_cmd, SMU_CMDSZ);
241: bus_dmamem_free(sc->sc_dmat, sc->sc_cmdseg, nseg);
242: return;
243: }
244:
245: rw_init(&sc->sc_lock, sc->sc_dev.dv_xname);
246:
247: /* Establish smu-doorbell interrupt. */
248: mac_intr_establish(parent, intr, IST_EDGE, IPL_BIO,
249: smu_intr, sc, sc->sc_dev.dv_xname);
250:
251: /* Initialize global variables that control RTC functionality. */
252: time_read = smu_time_read;
253: time_write = smu_time_write;
254:
255: /* Fans */
256: node = OF_getnodebyname(ca->ca_node, "rpm-fans");
257: if (node == 0)
258: node = OF_getnodebyname(ca->ca_node, "fans");
259: for (node = OF_child(node); node; node = OF_peer(node)) {
260: if (OF_getprop(node, "reg", ®, sizeof reg) <= 0 ||
261: OF_getprop(node, "device_type", type, sizeof type) <= 0)
262: continue;
263:
264: if (strcmp(type, "fan-rpm-control") != 0) {
265: printf(": unsupported fan type: %s\n", type);
266: return;
267: }
268:
269: if (sc->sc_num_fans >= SMU_MAXFANS) {
270: printf(": too many fans\n");
271: return;
272: }
273:
274: fan = &sc->sc_fans[sc->sc_num_fans++];
275: fan->sensor.type = SENSOR_FANRPM;
276: fan->sensor.flags = SENSOR_FINVALID;
277: fan->reg = reg;
278:
279: if (OF_getprop(node, "min-value", &val, sizeof val) <= 0)
280: val = 0;
281: fan->min_rpm = val;
282: if (OF_getprop(node, "max-value", &val, sizeof val) <= 0)
283: val = 0xffff;
284: fan->max_rpm = val;
285: if (OF_getprop(node, "unmanage-value", &val, sizeof val) <= 0)
286: val = fan->max_rpm;
287: fan->unmanaged_rpm = val;
288:
289: if (OF_getprop(node, "location", loc, sizeof loc) <= 0)
290: strlcpy(loc, "Unknown", sizeof loc);
291: strlcpy(fan->sensor.desc, loc, sizeof sensor->sensor.desc);
292:
293: /* Start running fans at their "unmanaged" speed. */
294: smu_fan_set_rpm(sc, fan, fan->unmanaged_rpm);
295:
296: sensor_attach(&sc->sc_sensordev, &fan->sensor);
297: }
298:
299: /*
300: * Bail out if we didn't find any fans. If we don't set the
301: * fans to a safe speed, but tickle the SMU periodically by
302: * reading sensors, the fans will never spin up and the
303: * machine might overheat.
304: */
305: if (sc->sc_num_fans == 0) {
306: printf(": no fans\n");
307: return;
308: }
309:
310: /* Sensors */
311: node = OF_getnodebyname(ca->ca_node, "sensors");
312: for (node = OF_child(node); node; node = OF_peer(node)) {
313: if (OF_getprop(node, "reg", &val, sizeof val) <= 0 ||
314: OF_getprop(node, "device_type", type, sizeof type) <= 0)
315: continue;
316:
317: sensor = &sc->sc_sensors[sc->sc_num_sensors++];
318: sensor->sensor.flags = SENSOR_FINVALID;
319: sensor->reg = val;
320:
321: if (strcmp(type, "current-sensor") == 0) {
322: sensor->sensor.type = SENSOR_AMPS;
323: } else if (strcmp(type, "temp-sensor") == 0) {
324: sensor->sensor.type = SENSOR_TEMP;
325: } else if (strcmp(type, "voltage-sensor") == 0) {
326: sensor->sensor.type = SENSOR_VOLTS_DC;
327: } else {
328: sensor->sensor.type = SENSOR_INTEGER;
329: }
330:
331: if (OF_getprop(node, "location", loc, sizeof loc) <= 0)
332: strlcpy(loc, "Unknown", sizeof loc);
333: strlcpy(sensor->sensor.desc, loc, sizeof sensor->sensor.desc);
334:
335: sensor_attach(&sc->sc_sensordev, &sensor->sensor);
336: }
337:
338: /* Register sensor device with sysctl */
339: strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
340: sizeof(sc->sc_sensordev.xname));
341: sensordev_install(&sc->sc_sensordev);
342:
343: /* CPU temperature diode calibration */
344: smu_get_datablock(sc, 0x18, data, sizeof data);
345: sc->sc_cpu_diode_scale = (data[4] << 8) + data[5];
346: sc->sc_cpu_diode_offset = (data[6] << 8) + data[7];
347:
348: /* CPU power (voltage and current) calibration */
349: smu_get_datablock(sc, 0x21, data, sizeof data);
350: sc->sc_cpu_volt_scale = (data[4] << 8) + data[5];
351: sc->sc_cpu_volt_offset = (data[6] << 8) + data[7];
352: sc->sc_cpu_curr_scale = (data[8] << 8) + data[9];
353: sc->sc_cpu_curr_offset = (data[10] << 8) + data[11];
354:
355: sensor_task_register(sc, smu_refresh_sensors, 5);
356: printf("\n");
357:
358: ppc64_slew_voltage = smu_slew_voltage;
359:
360: sc->sc_i2c_tag.ic_cookie = sc;
361: sc->sc_i2c_tag.ic_acquire_bus = smu_i2c_acquire_bus;
362: sc->sc_i2c_tag.ic_release_bus = smu_i2c_release_bus;
363: sc->sc_i2c_tag.ic_exec = smu_i2c_exec;
364:
365: node = OF_getnodebyname(ca->ca_node, "smu-i2c-control");
366: node = OF_child(node);
367:
368: bzero(&iba, sizeof iba);
369: iba.iba_name = "iic";
370: iba.iba_tag = &sc->sc_i2c_tag;
371: iba.iba_bus_scan = maciic_scan;
372: iba.iba_bus_scan_arg = &node;
373: config_found(&sc->sc_dev, &iba, NULL);
374: }
375:
376: int
377: smu_intr(void *arg)
378: {
379: wakeup(arg);
380: return 1;
381: }
382:
383: int
384: smu_do_cmd(struct smu_softc *sc, int timo)
385: {
386: struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd;
387: u_int8_t gpio, ack = ~cmd->cmd;
388: int error;
389:
390: /* Write to mailbox. */
391: bus_space_write_4(sc->sc_memt, sc->sc_buffh, 0,
392: sc->sc_cmdmap->dm_segs->ds_addr);
393:
394: /* Flush to RAM. */
395: asm __volatile__ ("dcbst 0,%0; sync" :: "r"(sc->sc_cmd): "memory");
396:
397: /* Ring doorbell. */
398: bus_space_write_1(sc->sc_memt, sc->sc_gpioh, 0, GPIO_DDR_OUTPUT);
399:
400: do {
401: error = tsleep(sc, PWAIT, "smu", (timo * hz) / 1000);
402: if (error)
403: return (error);
404: gpio = bus_space_read_1(sc->sc_memt, sc->sc_gpioh, 0);
405: } while (!(gpio & (GPIO_DATA)));
406:
407: /* CPU might have brought back the cache line. */
408: asm __volatile__ ("dcbf 0,%0; sync" :: "r"(sc->sc_cmd) : "memory");
409:
410: if (cmd->cmd != ack)
411: return (EIO);
412: return (0);
413: }
414:
415: int
416: smu_time_read(time_t *secs)
417: {
418: struct smu_softc *sc = smu_cd.cd_devs[0];
419: struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd;
420: struct clock_ymdhms dt;
421: int error;
422:
423: rw_enter_write(&sc->sc_lock);
424:
425: cmd->cmd = SMU_RTC;
426: cmd->len = 1;
427: cmd->data[0] = SMU_RTC_GET_DATETIME;
428: error = smu_do_cmd(sc, 800);
429: if (error) {
430: rw_exit_write(&sc->sc_lock);
431:
432: *secs = 0;
433: return (error);
434: }
435:
436: dt.dt_year = 2000 + FROMBCD(cmd->data[6]);
437: dt.dt_mon = FROMBCD(cmd->data[5]);
438: dt.dt_day = FROMBCD(cmd->data[4]);
439: dt.dt_hour = FROMBCD(cmd->data[2]);
440: dt.dt_min = FROMBCD(cmd->data[1]);
441: dt.dt_sec = FROMBCD(cmd->data[0]);
442:
443: rw_exit_write(&sc->sc_lock);
444:
445: *secs = clock_ymdhms_to_secs(&dt);
446: return (0);
447: }
448:
449: int
450: smu_time_write(time_t secs)
451: {
452: struct smu_softc *sc = smu_cd.cd_devs[0];
453: struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd;
454: struct clock_ymdhms dt;
455: int error;
456:
457: clock_secs_to_ymdhms(secs, &dt);
458:
459: rw_enter_write(&sc->sc_lock);
460:
461: cmd->cmd = SMU_RTC;
462: cmd->len = 8;
463: cmd->data[0] = SMU_RTC_SET_DATETIME;
464: cmd->data[1] = TOBCD(dt.dt_sec);
465: cmd->data[2] = TOBCD(dt.dt_min);
466: cmd->data[3] = TOBCD(dt.dt_hour);
467: cmd->data[4] = TOBCD(dt.dt_wday);
468: cmd->data[5] = TOBCD(dt.dt_day);
469: cmd->data[6] = TOBCD(dt.dt_mon);
470: cmd->data[7] = TOBCD(dt.dt_year - 2000);
471: error = smu_do_cmd(sc, 800);
472:
473: rw_exit_write(&sc->sc_lock);
474:
475: return (error);
476: }
477:
478:
479: int
480: smu_get_datablock(struct smu_softc *sc, u_int8_t id, u_int8_t *buf, size_t len)
481: {
482: struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd;
483: u_int8_t addr[4];
484: int error;
485:
486: cmd->cmd = SMU_PARTITION;
487: cmd->len = 2;
488: cmd->data[0] = SMU_PARTITION_LATEST;
489: cmd->data[1] = id;
490: error = smu_do_cmd(sc, 800);
491: if (error)
492: return (error);
493:
494: addr[0] = 0x00;
495: addr[1] = 0x00;
496: addr[2] = cmd->data[0];
497: addr[3] = cmd->data[1];
498:
499: cmd->cmd = SMU_MISC;
500: cmd->len = 7;
501: cmd->data[0] = SMU_MISC_GET_DATA;
502: cmd->data[1] = sizeof(u_int32_t);
503: cmd->data[2] = addr[0];
504: cmd->data[3] = addr[1];
505: cmd->data[4] = addr[2];
506: cmd->data[5] = addr[3];
507: cmd->data[6] = len;
508: error = smu_do_cmd(sc, 800);
509: if (error)
510: return (error);
511:
512: memcpy(buf, cmd->data, len);
513: return (0);
514: }
515:
516: int
517: smu_fan_set_rpm(struct smu_softc *sc, struct smu_fan *fan, u_int16_t rpm)
518: {
519: struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd;
520:
521: /*
522: * On the PowerMac8,2 this command expects the requested fan
523: * speed at a different location in the command block than on
524: * the PowerMac8,1. We simply store the value at both
525: * locations.
526: */
527: cmd->cmd = SMU_FAN;
528: cmd->len = 14;
529: cmd->data[0] = 0x00; /* fan-rpm-control */
530: cmd->data[1] = 0x01 << fan->reg;
531: cmd->data[2] = cmd->data[2 + fan->reg * 2] = (rpm >> 8) & 0xff;
532: cmd->data[3] = cmd->data[3 + fan->reg * 2] = (rpm & 0xff);
533: return smu_do_cmd(sc, 800);
534: }
535:
536: int
537: smu_fan_refresh(struct smu_softc *sc, struct smu_fan *fan)
538: {
539: struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd;
540: int error;
541:
542: cmd->cmd = SMU_FAN;
543: cmd->len = 2;
544: cmd->data[0] = 0x01; /* fan-rpm-control */
545: cmd->data[1] = 0x01 << fan->reg;
546: error = smu_do_cmd(sc, 800);
547: if (error) {
548: fan->sensor.flags = SENSOR_FINVALID;
549: return (error);
550: }
551: fan->sensor.value = (cmd->data[1] << 8) + cmd->data[2];
552: fan->sensor.flags = 0;
553: return (0);
554: }
555:
556: int
557: smu_sensor_refresh(struct smu_softc *sc, struct smu_sensor *sensor)
558: {
559: struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd;
560: int64_t value;
561: int error;
562:
563: cmd->cmd = SMU_ADC;
564: cmd->len = 1;
565: cmd->data[0] = sensor->reg;
566: error = smu_do_cmd(sc, 800);
567: if (error) {
568: sensor->sensor.flags = SENSOR_FINVALID;
569: return (error);
570: }
571: value = (cmd->data[0] << 8) + cmd->data[1];
572: switch (sensor->sensor.type) {
573: case SENSOR_TEMP:
574: value *= sc->sc_cpu_diode_scale;
575: value >>= 3;
576: value += ((int64_t)sc->sc_cpu_diode_offset) << 9;
577: value <<= 1;
578:
579: /* Convert from 16.16 fixed point degC into muK. */
580: value *= 15625;
581: value /= 1024;
582: value += 273150000;
583: break;
584:
585: case SENSOR_VOLTS_DC:
586: value *= sc->sc_cpu_volt_scale;
587: value += sc->sc_cpu_volt_offset;
588: value <<= 4;
589:
590: /* Convert from 16.16 fixed point V into muV. */
591: value *= 15625;
592: value /= 1024;
593: break;
594:
595: case SENSOR_AMPS:
596: value *= sc->sc_cpu_curr_scale;
597: value += sc->sc_cpu_curr_offset;
598: value <<= 4;
599:
600: /* Convert from 16.16 fixed point A into muA. */
601: value *= 15625;
602: value /= 1024;
603: break;
604:
605: default:
606: break;
607: }
608: sensor->sensor.value = value;
609: sensor->sensor.flags = 0;
610: return (0);
611: }
612:
613: void
614: smu_refresh_sensors(void *arg)
615: {
616: struct smu_softc *sc = arg;
617: int i;
618:
619: rw_enter_write(&sc->sc_lock);
620: for (i = 0; i < sc->sc_num_sensors; i++)
621: smu_sensor_refresh(sc, &sc->sc_sensors[i]);
622: for (i = 0; i < sc->sc_num_fans; i++)
623: smu_fan_refresh(sc, &sc->sc_fans[i]);
624: rw_exit_write(&sc->sc_lock);
625: }
626:
627: int
628: smu_i2c_acquire_bus(void *cookie, int flags)
629: {
630: struct smu_softc *sc = cookie;
631:
632: if (flags & I2C_F_POLL)
633: return (0);
634:
635: return (rw_enter(&sc->sc_lock, RW_WRITE));
636: }
637:
638: void
639: smu_i2c_release_bus(void *cookie, int flags)
640: {
641: struct smu_softc *sc = cookie;
642:
643: if (flags & I2C_F_POLL)
644: return;
645:
646: rw_exit(&sc->sc_lock);
647: }
648:
649: int
650: smu_i2c_exec(void *cookie, i2c_op_t op, i2c_addr_t addr,
651: const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags)
652: {
653: struct smu_softc *sc = cookie;
654: struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd;
655: u_int8_t smu_op = SMU_I2C_NORMAL;
656: int error, retries = 10;
657:
658: if (!I2C_OP_STOP_P(op) || cmdlen > 3 || len > 5)
659: return (EINVAL);
660:
661: if(cmdlen == 0)
662: smu_op = SMU_I2C_SIMPLE;
663: else if (I2C_OP_READ_P(op))
664: smu_op = SMU_I2C_COMBINED;
665:
666: cmd->cmd = SMU_I2C;
667: cmd->len = 9 + len;
668: cmd->data[0] = 0xb;
669: cmd->data[1] = smu_op;
670: cmd->data[2] = addr << 1;
671: cmd->data[3] = cmdlen;
672: memcpy (&cmd->data[4], cmdbuf, cmdlen);
673: cmd->data[7] = addr << 1 | I2C_OP_READ_P(op);
674: cmd->data[8] = len;
675: memcpy(&cmd->data[9], buf, len);
676:
677: error = smu_do_cmd(sc, 250);
678: if (error)
679: return error;
680:
681: while (retries--) {
682: cmd->cmd = SMU_I2C;
683: cmd->len = 1;
684: cmd->data[0] = 0;
685: memset(&cmd->data[1], 0xff, len);
686:
687: error = smu_do_cmd(sc, 250);
688: if (error)
689: return error;
690:
691: if ((cmd->data[0] & 0x80) == 0)
692: break;
693: if (cmd->data[0] == 0xfd)
694: break;
695:
696: DELAY(15 * 1000);
697: }
698:
699: if (cmd->data[0] & 0x80)
700: return (EIO);
701:
702: if (I2C_OP_READ_P(op))
703: memcpy(buf, &cmd->data[1], len);
704: return (0);
705: }
706:
707: void
708: smu_slew_voltage(u_int freq_scale)
709: {
710: struct smu_softc *sc = smu_cd.cd_devs[0];
711: struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd;
712:
713: cmd->cmd = SMU_POWER;
714: cmd->len = 8;
715: memcpy(cmd->data, "VSLEW", 5);
716: cmd->data[5] = 0xff;
717: cmd->data[6] = 1;
718: cmd->data[7] = freq_scale;
719:
720: smu_do_cmd(sc, 250);
721: }
CVSweb