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