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