smu.c revision 1.14
1/*	$OpenBSD: smu.c,v 1.14 2007/03/01 20:54:33 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/lock.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 sensor	sensor;
47};
48
49#define SMU_MAXSENSORS	3
50
51struct smu_sensor {
52	u_int8_t	reg;
53	struct sensor	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 lock	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 sensordev 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
90struct cfattach smu_ca = {
91        sizeof(struct smu_softc), smu_match, smu_attach
92};
93
94struct cfdriver smu_cd = {
95        NULL, "smu", DV_DULL,
96};
97
98/* SMU command */
99struct 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
136int	smu_intr(void *);
137
138int	smu_do_cmd(struct smu_softc *, int);
139int	smu_time_read(time_t *);
140int	smu_time_write(time_t);
141int	smu_get_datablock(struct smu_softc *sc, u_int8_t, u_int8_t *, size_t);
142int	smu_fan_set_rpm(struct smu_softc *, struct smu_fan *, u_int16_t);
143int	smu_fan_refresh(struct smu_softc *, struct smu_fan *);
144int	smu_sensor_refresh(struct smu_softc *, struct smu_sensor *);
145void	smu_refresh_sensors(void *);
146
147int	smu_i2c_acquire_bus(void *, int);
148void	smu_i2c_release_bus(void *, int);
149int	smu_i2c_exec(void *, i2c_op_t, i2c_addr_t,
150	    const void *, size_t, void *buf, size_t, int);
151
152void	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
162int
163smu_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 */
173extern struct powerpc_bus_dma_tag pci_bus_dma_tag;
174
175void
176smu_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", &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	lockinit(&sc->sc_lock, PZERO, sc->sc_dev.dv_xname, 0, 0);
246
247	/* Establish smu-doorbell interrupt. */
248	mac_intr_establish(parent, intr, IST_EDGE, IPL_BIO,
249	    smu_intr, sc, "smu");
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, "fans");
257	for (node = OF_child(node); node; node = OF_peer(node)) {
258		if (OF_getprop(node, "reg", &reg, sizeof reg) <= 0 ||
259		    OF_getprop(node, "device_type", type, sizeof type) <= 0)
260			continue;
261
262		if (strcmp(type, "fan-rpm-control") != 0) {
263			printf(": unsupported fan type: %s\n", type);
264			return;
265		}
266
267		if (sc->sc_num_fans >= SMU_MAXFANS) {
268			printf(": too many fans\n");
269			return;
270		}
271
272		fan = &sc->sc_fans[sc->sc_num_fans++];
273		fan->sensor.type = SENSOR_FANRPM;
274		fan->sensor.flags = SENSOR_FINVALID;
275		fan->reg = reg;
276
277		if (OF_getprop(node, "min-value", &val, sizeof val) <= 0)
278			val = 0;
279		fan->min_rpm = val;
280		if (OF_getprop(node, "max-value", &val, sizeof val) <= 0)
281			val = 0xffff;
282		fan->max_rpm = val;
283		if (OF_getprop(node, "unmanage-value", &val, sizeof val) <= 0)
284			val = fan->max_rpm;
285		fan->unmanaged_rpm = val;
286
287		if (OF_getprop(node, "location", loc, sizeof loc) <= 0)
288			strlcpy(loc, "Unknown", sizeof loc);
289		strlcpy(fan->sensor.desc, loc, sizeof sensor->sensor.desc);
290
291		/* Start running fans at their "unmanaged" speed. */
292		smu_fan_set_rpm(sc, fan, fan->unmanaged_rpm);
293
294		sensor_attach(&sc->sc_sensordev, &fan->sensor);
295	}
296
297	/*
298	 * Bail out if we didn't find any fans.  If we don't set the
299	 * fans to a safe speed, but tickle the SMU periodically by
300	 * reading sensors, the fans will never spin up and the
301	 * machine might overheat.
302	 */
303	if (sc->sc_num_fans == 0) {
304		printf(": no fans\n");
305		return;
306	}
307
308	/* Sensors */
309	node = OF_getnodebyname(ca->ca_node, "sensors");
310	for (node = OF_child(node); node; node = OF_peer(node)) {
311		if (OF_getprop(node, "reg", &val, sizeof val) <= 0 ||
312		    OF_getprop(node, "device_type", type, sizeof type) <= 0)
313			continue;
314
315		sensor = &sc->sc_sensors[sc->sc_num_sensors++];
316		sensor->sensor.flags = SENSOR_FINVALID;
317		sensor->reg = val;
318
319		if (strcmp(type, "current-sensor") == 0) {
320			sensor->sensor.type = SENSOR_AMPS;
321		} else if (strcmp(type, "temp-sensor") == 0) {
322			sensor->sensor.type = SENSOR_TEMP;
323		} else if (strcmp(type, "voltage-sensor") == 0) {
324			sensor->sensor.type = SENSOR_VOLTS_DC;
325		} else {
326			sensor->sensor.type = SENSOR_INTEGER;
327		}
328
329		if (OF_getprop(node, "location", loc, sizeof loc) <= 0)
330			strlcpy(loc, "Unknown", sizeof loc);
331		strlcpy(sensor->sensor.desc, loc, sizeof sensor->sensor.desc);
332
333		sensor_attach(&sc->sc_sensordev, &sensor->sensor);
334	}
335
336	/* Register sensor device with sysctl */
337	strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
338	    sizeof(sc->sc_sensordev.xname));
339	sensordev_install(&sc->sc_sensordev);
340
341	/* CPU temperature diode calibration */
342	smu_get_datablock(sc, 0x18, data, sizeof data);
343	sc->sc_cpu_diode_scale = (data[4] << 8) + data[5];
344	sc->sc_cpu_diode_offset = (data[6] << 8) + data[7];
345
346	/* CPU power (voltage and current) calibration */
347	smu_get_datablock(sc, 0x21, data, sizeof data);
348	sc->sc_cpu_volt_scale = (data[4] << 8) + data[5];
349	sc->sc_cpu_volt_offset = (data[6] << 8) + data[7];
350	sc->sc_cpu_curr_scale = (data[8] << 8) + data[9];
351	sc->sc_cpu_curr_offset = (data[10] << 8) + data[11];
352
353	sensor_task_register(sc, smu_refresh_sensors, 5);
354	printf("\n");
355
356	ppc64_slew_voltage = smu_slew_voltage;
357
358	sc->sc_i2c_tag.ic_cookie = sc;
359	sc->sc_i2c_tag.ic_acquire_bus = smu_i2c_acquire_bus;
360	sc->sc_i2c_tag.ic_release_bus = smu_i2c_release_bus;
361	sc->sc_i2c_tag.ic_exec = smu_i2c_exec;
362
363	node = OF_getnodebyname(ca->ca_node, "smu-i2c-control");
364	node = OF_child(node);
365
366	bzero(&iba, sizeof iba);
367	iba.iba_name = "iic";
368	iba.iba_tag = &sc->sc_i2c_tag;
369	iba.iba_bus_scan = maciic_scan;
370	iba.iba_bus_scan_arg = &node;
371	config_found(&sc->sc_dev, &iba, NULL);
372}
373
374int
375smu_intr(void *arg)
376{
377	wakeup(arg);
378	return 1;
379}
380
381int
382smu_do_cmd(struct smu_softc *sc, int timo)
383{
384	struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd;
385	u_int8_t gpio, ack = ~cmd->cmd;
386	int error;
387
388	/* Write to mailbox.  */
389	bus_space_write_4(sc->sc_memt, sc->sc_buffh, 0,
390	    sc->sc_cmdmap->dm_segs->ds_addr);
391
392	/* Flush to RAM. */
393	asm __volatile__ ("dcbst 0,%0; sync" :: "r"(sc->sc_cmd): "memory");
394
395	/* Ring doorbell.  */
396	bus_space_write_1(sc->sc_memt, sc->sc_gpioh, 0, GPIO_DDR_OUTPUT);
397
398	do {
399		error = tsleep(sc, PWAIT, "smu", (timo * hz) / 1000);
400		if (error)
401			return (error);
402		gpio = bus_space_read_1(sc->sc_memt, sc->sc_gpioh, 0);
403	} while (!(gpio & (GPIO_DATA)));
404
405	/* CPU might have brought back the cache line. */
406	asm __volatile__ ("dcbf 0,%0; sync" :: "r"(sc->sc_cmd) : "memory");
407
408	if (cmd->cmd != ack)
409		return (EIO);
410	return (0);
411}
412
413int
414smu_time_read(time_t *secs)
415{
416	struct smu_softc *sc = smu_cd.cd_devs[0];
417	struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd;
418	struct clock_ymdhms dt;
419	int error;
420
421	lockmgr(&sc->sc_lock, LK_EXCLUSIVE, NULL);
422
423	cmd->cmd = SMU_RTC;
424	cmd->len = 1;
425	cmd->data[0] = SMU_RTC_GET_DATETIME;
426	error = smu_do_cmd(sc, 800);
427	if (error) {
428		lockmgr(&sc->sc_lock, LK_RELEASE, NULL);
429
430		*secs = 0;
431		return (error);
432	}
433
434	dt.dt_year = 2000 + FROMBCD(cmd->data[6]);
435	dt.dt_mon = FROMBCD(cmd->data[5]);
436	dt.dt_day = FROMBCD(cmd->data[4]);
437	dt.dt_hour = FROMBCD(cmd->data[2]);
438	dt.dt_min = FROMBCD(cmd->data[1]);
439	dt.dt_sec = FROMBCD(cmd->data[0]);
440
441	lockmgr(&sc->sc_lock, LK_RELEASE, NULL);
442
443	*secs = clock_ymdhms_to_secs(&dt);
444	return (0);
445}
446
447int
448smu_time_write(time_t secs)
449{
450	struct smu_softc *sc = smu_cd.cd_devs[0];
451	struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd;
452	struct clock_ymdhms dt;
453	int error;
454
455	clock_secs_to_ymdhms(secs, &dt);
456
457	lockmgr(&sc->sc_lock, LK_EXCLUSIVE, NULL);
458
459	cmd->cmd = SMU_RTC;
460	cmd->len = 8;
461	cmd->data[0] = SMU_RTC_SET_DATETIME;
462	cmd->data[1] = TOBCD(dt.dt_sec);
463	cmd->data[2] = TOBCD(dt.dt_min);
464	cmd->data[3] = TOBCD(dt.dt_hour);
465	cmd->data[4] = TOBCD(dt.dt_wday);
466	cmd->data[5] = TOBCD(dt.dt_day);
467	cmd->data[6] = TOBCD(dt.dt_mon);
468	cmd->data[7] = TOBCD(dt.dt_year - 2000);
469	error = smu_do_cmd(sc, 800);
470
471	lockmgr(&sc->sc_lock, LK_RELEASE, NULL);
472
473	return (error);
474}
475
476
477int
478smu_get_datablock(struct smu_softc *sc, u_int8_t id, u_int8_t *buf, size_t len)
479{
480	struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd;
481	u_int8_t addr[4];
482	int error;
483
484	cmd->cmd = SMU_PARTITION;
485	cmd->len = 2;
486	cmd->data[0] = SMU_PARTITION_LATEST;
487	cmd->data[1] = id;
488	error = smu_do_cmd(sc, 800);
489	if (error)
490		return (error);
491
492	addr[0] = 0x00;
493	addr[1] = 0x00;
494	addr[2] = cmd->data[0];
495	addr[3] = cmd->data[1];
496
497	cmd->cmd = SMU_MISC;
498	cmd->len = 7;
499	cmd->data[0] = SMU_MISC_GET_DATA;
500	cmd->data[1] = sizeof(u_int32_t);
501	cmd->data[2] = addr[0];
502	cmd->data[3] = addr[1];
503	cmd->data[4] = addr[2];
504	cmd->data[5] = addr[3];
505	cmd->data[6] = len;
506	error = smu_do_cmd(sc, 800);
507	if (error)
508		return (error);
509
510	memcpy(buf, cmd->data, len);
511	return (0);
512}
513
514int
515smu_fan_set_rpm(struct smu_softc *sc, struct smu_fan *fan, u_int16_t rpm)
516{
517	struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd;
518
519	cmd->cmd = SMU_FAN;
520	cmd->len = 4;
521	cmd->data[0] = 0x00;	/* fan-rpm-control */
522	cmd->data[1] = 0x01 << fan->reg;
523	cmd->data[2] = (rpm >> 8) & 0xff;
524	cmd->data[3] = (rpm & 0xff);
525	return smu_do_cmd(sc, 800);
526}
527
528int
529smu_fan_refresh(struct smu_softc *sc, struct smu_fan *fan)
530{
531	struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd;
532	int error;
533
534	cmd->cmd = SMU_FAN;
535	cmd->len = 2;
536	cmd->data[0] = 0x01;	/* fan-rpm-control */
537	cmd->data[1] = 0x01 << fan->reg;
538	error = smu_do_cmd(sc, 800);
539	if (error) {
540		fan->sensor.flags = SENSOR_FINVALID;
541		return (error);
542	}
543	fan->sensor.value = (cmd->data[1] << 8) + cmd->data[2];
544	fan->sensor.flags = 0;
545	return (0);
546}
547
548int
549smu_sensor_refresh(struct smu_softc *sc, struct smu_sensor *sensor)
550{
551	struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd;
552	int64_t value;
553	int error;
554
555	cmd->cmd = SMU_ADC;
556	cmd->len = 1;
557	cmd->data[0] = sensor->reg;
558	error = smu_do_cmd(sc, 800);
559	if (error) {
560		sensor->sensor.flags = SENSOR_FINVALID;
561		return (error);
562	}
563	value = (cmd->data[0] << 8) + cmd->data[1];
564	switch (sensor->sensor.type) {
565	case SENSOR_TEMP:
566		value *= sc->sc_cpu_diode_scale;
567		value >>= 3;
568		value += ((int64_t)sc->sc_cpu_diode_offset) << 9;
569		value <<= 1;
570
571		/* Convert from 16.16 fixed point degC into muK. */
572		value *= 15625;
573		value /= 1024;
574		value += 273150000;
575		break;
576
577	case SENSOR_VOLTS_DC:
578		value *= sc->sc_cpu_volt_scale;
579		value += sc->sc_cpu_volt_offset;
580		value <<= 4;
581
582		/* Convert from 16.16 fixed point V into muV. */
583		value *= 15625;
584		value /= 1024;
585		break;
586
587	case SENSOR_AMPS:
588		value *= sc->sc_cpu_curr_scale;
589		value += sc->sc_cpu_curr_offset;
590		value <<= 4;
591
592		/* Convert from 16.16 fixed point A into muA. */
593		value *= 15625;
594		value /= 1024;
595		break;
596
597	default:
598		break;
599	}
600	sensor->sensor.value = value;
601	sensor->sensor.flags = 0;
602	return (0);
603}
604
605void
606smu_refresh_sensors(void *arg)
607{
608	struct smu_softc *sc = arg;
609	int i;
610
611	lockmgr(&sc->sc_lock, LK_EXCLUSIVE, NULL);
612	for (i = 0; i < sc->sc_num_sensors; i++)
613		smu_sensor_refresh(sc, &sc->sc_sensors[i]);
614	for (i = 0; i < sc->sc_num_fans; i++)
615		smu_fan_refresh(sc, &sc->sc_fans[i]);
616	lockmgr(&sc->sc_lock, LK_RELEASE, NULL);
617}
618
619int
620smu_i2c_acquire_bus(void *cookie, int flags)
621{
622	struct smu_softc *sc = cookie;
623
624	if (flags & I2C_F_POLL)
625		return (0);
626
627	return (lockmgr(&sc->sc_lock, LK_EXCLUSIVE, NULL));
628}
629
630void
631smu_i2c_release_bus(void *cookie, int flags)
632{
633	struct smu_softc *sc = cookie;
634
635        if (flags & I2C_F_POLL)
636                return;
637
638        lockmgr(&sc->sc_lock, LK_RELEASE, NULL);
639}
640
641int
642smu_i2c_exec(void *cookie, i2c_op_t op, i2c_addr_t addr,
643    const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags)
644{
645	struct smu_softc *sc = cookie;
646	struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd;
647	u_int8_t smu_op = SMU_I2C_NORMAL;
648	int error, retries = 10;
649
650	if (!I2C_OP_STOP_P(op) || cmdlen > 3 || len > 5)
651		return (EINVAL);
652
653	if(cmdlen == 0)
654		smu_op = SMU_I2C_SIMPLE;
655	else if (I2C_OP_READ_P(op))
656		smu_op = SMU_I2C_COMBINED;
657
658	cmd->cmd = SMU_I2C;
659	cmd->len = 9 + len;
660	cmd->data[0] = 0xb;
661	cmd->data[1] = smu_op;
662	cmd->data[2] = addr << 1;
663	cmd->data[3] = cmdlen;
664	memcpy (&cmd->data[4], cmdbuf, cmdlen);
665	cmd->data[7] = addr << 1 | I2C_OP_READ_P(op);
666	cmd->data[8] = len;
667	memcpy(&cmd->data[9], buf, len);
668
669	error = smu_do_cmd(sc, 250);
670	if (error)
671		return error;
672
673	while (retries--) {
674		cmd->cmd = SMU_I2C;
675		cmd->len = 1;
676		cmd->data[0] = 0;
677		memset(&cmd->data[1], 0xff, len);
678
679		error = smu_do_cmd(sc, 250);
680		if (error)
681			return error;
682
683		if ((cmd->data[0] & 0x80) == 0)
684			break;
685		if (cmd->data[0] == 0xfd)
686			break;
687
688		DELAY(15 * 1000);
689	}
690
691	if (cmd->data[0] & 0x80)
692		return (EIO);
693
694	if (I2C_OP_READ_P(op))
695		memcpy(buf, &cmd->data[1], len);
696	return (0);
697}
698
699void
700smu_slew_voltage(u_int freq_scale)
701{
702	struct smu_softc *sc = smu_cd.cd_devs[0];
703	struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd;
704
705	cmd->cmd = SMU_POWER;
706	cmd->len = 8;
707	memcpy(cmd->data, "VSLEW", 5);
708	cmd->data[5] = 0xff;
709	cmd->data[6] = 1;
710	cmd->data[7] = freq_scale;
711
712	smu_do_cmd(sc, 250);
713}
714