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