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