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