1*89ed722cSmpi /* $OpenBSD: smu.c,v 1.35 2022/03/13 12:33:01 mpi Exp $ */
226c1ffa3Skettenis
326c1ffa3Skettenis /*
426c1ffa3Skettenis * Copyright (c) 2005 Mark Kettenis
526c1ffa3Skettenis *
626c1ffa3Skettenis * Permission to use, copy, modify, and distribute this software for any
726c1ffa3Skettenis * purpose with or without fee is hereby granted, provided that the above
826c1ffa3Skettenis * copyright notice and this permission notice appear in all copies.
926c1ffa3Skettenis *
1026c1ffa3Skettenis * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1126c1ffa3Skettenis * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1226c1ffa3Skettenis * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1326c1ffa3Skettenis * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1426c1ffa3Skettenis * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1526c1ffa3Skettenis * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1626c1ffa3Skettenis * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1726c1ffa3Skettenis */
1826c1ffa3Skettenis
1926c1ffa3Skettenis #include <sys/param.h>
2026c1ffa3Skettenis #include <sys/systm.h>
2126c1ffa3Skettenis #include <sys/device.h>
221e8e22a4Skettenis #include <sys/kernel.h>
23769768baSthib #include <sys/rwlock.h>
241e8e22a4Skettenis #include <sys/proc.h>
251e8e22a4Skettenis #include <sys/sensors.h>
2626c1ffa3Skettenis
2726c1ffa3Skettenis #include <machine/autoconf.h>
28b6032f37Skettenis #include <machine/cpu.h>
2926c1ffa3Skettenis
3026c1ffa3Skettenis #include <dev/clock_subr.h>
312e975dd8Skettenis #include <dev/i2c/i2cvar.h>
3226c1ffa3Skettenis #include <dev/ofw/openfirm.h>
3326c1ffa3Skettenis
3439dd359fSmpi #include <macppc/dev/maci2cvar.h>
35309d5690Smglocker #include <macppc/dev/thermal.h>
3639dd359fSmpi #include <macppc/pci/macobio.h>
372e975dd8Skettenis
3826c1ffa3Skettenis int smu_match(struct device *, void *, void *);
3926c1ffa3Skettenis void smu_attach(struct device *, struct device *, void *);
4026c1ffa3Skettenis
41309d5690Smglocker /* Target and Max. temperature in muK. */
42309d5690Smglocker #define TEMP_TRG 38 * 1000000 + 273150000
43309d5690Smglocker #define TEMP_MAX 70 * 1000000 + 273150000
44309d5690Smglocker
45e26d369eSmpi #define SMU_MAXFANS 8
461e8e22a4Skettenis
471e8e22a4Skettenis struct smu_fan {
48309d5690Smglocker struct thermal_fan fan;
491e8e22a4Skettenis u_int8_t reg;
501e8e22a4Skettenis u_int16_t min_rpm;
511e8e22a4Skettenis u_int16_t max_rpm;
521e8e22a4Skettenis u_int16_t unmanaged_rpm;
53528e43b2Smglocker u_int16_t min_pwm;
54528e43b2Smglocker u_int16_t max_pwm;
55528e43b2Smglocker u_int16_t unmanaged_pwm;
56275cbf62Sderaadt struct ksensor sensor;
571e8e22a4Skettenis };
581e8e22a4Skettenis
59e242de35Skettenis #define SMU_MAXSENSORS 4
601e8e22a4Skettenis
611e8e22a4Skettenis struct smu_sensor {
62309d5690Smglocker struct thermal_temp therm;
631e8e22a4Skettenis u_int8_t reg;
64275cbf62Sderaadt struct ksensor sensor;
651e8e22a4Skettenis };
661e8e22a4Skettenis
6726c1ffa3Skettenis struct smu_softc {
6826c1ffa3Skettenis struct device sc_dev;
6926c1ffa3Skettenis
7026c1ffa3Skettenis /* SMU command buffer. */
7126c1ffa3Skettenis bus_dma_tag_t sc_dmat;
7226c1ffa3Skettenis bus_dmamap_t sc_cmdmap;
7326c1ffa3Skettenis bus_dma_segment_t sc_cmdseg[1];
7426c1ffa3Skettenis caddr_t sc_cmd;
75769768baSthib struct rwlock sc_lock;
7626c1ffa3Skettenis
7726c1ffa3Skettenis /* Doorbell and mailbox. */
7826c1ffa3Skettenis struct ppc_bus_space sc_mem_bus_space;
7926c1ffa3Skettenis bus_space_tag_t sc_memt;
8026c1ffa3Skettenis bus_space_handle_t sc_gpioh;
8126c1ffa3Skettenis bus_space_handle_t sc_buffh;
821e8e22a4Skettenis
83d5fb2debSmglocker uint8_t sc_firmware_old;
84d5fb2debSmglocker
851e8e22a4Skettenis struct smu_fan sc_fans[SMU_MAXFANS];
861e8e22a4Skettenis int sc_num_fans;
871e8e22a4Skettenis
881e8e22a4Skettenis struct smu_sensor sc_sensors[SMU_MAXSENSORS];
891e8e22a4Skettenis int sc_num_sensors;
901e8e22a4Skettenis
91275cbf62Sderaadt struct ksensordev sc_sensordev;
9227515a6bSderaadt
931e8e22a4Skettenis u_int16_t sc_cpu_diode_scale;
941e8e22a4Skettenis int16_t sc_cpu_diode_offset;
951e8e22a4Skettenis u_int16_t sc_cpu_volt_scale;
961e8e22a4Skettenis int16_t sc_cpu_volt_offset;
971e8e22a4Skettenis u_int16_t sc_cpu_curr_scale;
981e8e22a4Skettenis int16_t sc_cpu_curr_offset;
992e975dd8Skettenis
1000367fc9dSkettenis u_int16_t sc_slots_pow_scale;
1010367fc9dSkettenis int16_t sc_slots_pow_offset;
1020367fc9dSkettenis
1032e975dd8Skettenis struct i2c_controller sc_i2c_tag;
10426c1ffa3Skettenis };
10526c1ffa3Skettenis
106*89ed722cSmpi const struct cfattach smu_ca = {
10726c1ffa3Skettenis sizeof(struct smu_softc), smu_match, smu_attach
10826c1ffa3Skettenis };
1091e8e22a4Skettenis
11026c1ffa3Skettenis struct cfdriver smu_cd = {
11126c1ffa3Skettenis NULL, "smu", DV_DULL,
11226c1ffa3Skettenis };
11326c1ffa3Skettenis
1141e8e22a4Skettenis /* SMU command */
11526c1ffa3Skettenis struct smu_cmd {
11626c1ffa3Skettenis u_int8_t cmd;
11726c1ffa3Skettenis u_int8_t len;
11826c1ffa3Skettenis u_int8_t data[254];
11926c1ffa3Skettenis };
12026c1ffa3Skettenis #define SMU_CMDSZ sizeof(struct smu_cmd)
12126c1ffa3Skettenis
1221e8e22a4Skettenis /* RTC */
12326c1ffa3Skettenis #define SMU_RTC 0x8e
12426c1ffa3Skettenis #define SMU_RTC_SET_DATETIME 0x80
12526c1ffa3Skettenis #define SMU_RTC_GET_DATETIME 0x81
12626c1ffa3Skettenis
1271e8e22a4Skettenis /* ADC */
1281e8e22a4Skettenis #define SMU_ADC 0xd8
1291e8e22a4Skettenis
1301e8e22a4Skettenis /* Fan control */
1311e8e22a4Skettenis #define SMU_FAN 0x4a
1321e8e22a4Skettenis
1331e8e22a4Skettenis /* Data partitions */
1341e8e22a4Skettenis #define SMU_PARTITION 0x3e
1351e8e22a4Skettenis #define SMU_PARTITION_LATEST 0x01
1361e8e22a4Skettenis #define SMU_PARTITION_BASE 0x02
1371e8e22a4Skettenis #define SMU_PARTITION_UPDATE 0x03
1381e8e22a4Skettenis
1392e975dd8Skettenis /* I2C */
1402e975dd8Skettenis #define SMU_I2C 0x9a
1412e975dd8Skettenis #define SMU_I2C_SIMPLE 0x00
1422e975dd8Skettenis #define SMU_I2C_NORMAL 0x01
1432e975dd8Skettenis #define SMU_I2C_COMBINED 0x02
1442e975dd8Skettenis
145b6032f37Skettenis /* Power Management */
146b6032f37Skettenis #define SMU_POWER 0xaa
147b6032f37Skettenis
1481e8e22a4Skettenis /* Miscellaneous */
1491e8e22a4Skettenis #define SMU_MISC 0xee
1501e8e22a4Skettenis #define SMU_MISC_GET_DATA 0x02
1511e8e22a4Skettenis
1521e8e22a4Skettenis int smu_intr(void *);
1531e8e22a4Skettenis
15426c1ffa3Skettenis int smu_do_cmd(struct smu_softc *, int);
15526c1ffa3Skettenis int smu_time_read(time_t *);
15626c1ffa3Skettenis int smu_time_write(time_t);
1571e8e22a4Skettenis int smu_get_datablock(struct smu_softc *sc, u_int8_t, u_int8_t *, size_t);
158d5fb2debSmglocker void smu_firmware_probe(struct smu_softc *, struct smu_fan *);
1591e8e22a4Skettenis int smu_fan_set_rpm(struct smu_softc *, struct smu_fan *, u_int16_t);
160528e43b2Smglocker int smu_fan_set_pwm(struct smu_softc *, struct smu_fan *, u_int16_t);
161a16ae0caSmglocker int smu_fan_read_rpm(struct smu_softc *, struct smu_fan *, u_int16_t *);
162528e43b2Smglocker int smu_fan_read_pwm(struct smu_softc *, struct smu_fan *, u_int16_t *,
163528e43b2Smglocker u_int16_t *);
1641e8e22a4Skettenis int smu_fan_refresh(struct smu_softc *, struct smu_fan *);
165309d5690Smglocker int smu_sensor_refresh(struct smu_softc *, struct smu_sensor *, int);
1661e8e22a4Skettenis void smu_refresh_sensors(void *);
16726c1ffa3Skettenis
168309d5690Smglocker int smu_fan_set_rpm_thermal(struct smu_fan *, int);
169309d5690Smglocker int smu_fan_set_pwm_thermal(struct smu_fan *, int);
170309d5690Smglocker int smu_sensor_refresh_thermal(struct smu_sensor *);
171309d5690Smglocker
1722e975dd8Skettenis int smu_i2c_acquire_bus(void *, int);
1732e975dd8Skettenis void smu_i2c_release_bus(void *, int);
1742e975dd8Skettenis int smu_i2c_exec(void *, i2c_op_t, i2c_addr_t,
1752e975dd8Skettenis const void *, size_t, void *buf, size_t, int);
1762e975dd8Skettenis
177b6032f37Skettenis void smu_slew_voltage(u_int);
178b6032f37Skettenis
17926c1ffa3Skettenis int
smu_match(struct device * parent,void * cf,void * aux)18026c1ffa3Skettenis smu_match(struct device *parent, void *cf, void *aux)
18126c1ffa3Skettenis {
18226c1ffa3Skettenis struct confargs *ca = aux;
18326c1ffa3Skettenis
18426c1ffa3Skettenis if (strcmp(ca->ca_name, "smu") == 0)
18526c1ffa3Skettenis return (1);
18626c1ffa3Skettenis return (0);
18726c1ffa3Skettenis }
18826c1ffa3Skettenis
18926c1ffa3Skettenis /* XXX */
19026c1ffa3Skettenis extern struct powerpc_bus_dma_tag pci_bus_dma_tag;
19126c1ffa3Skettenis
19226c1ffa3Skettenis void
smu_attach(struct device * parent,struct device * self,void * aux)19326c1ffa3Skettenis smu_attach(struct device *parent, struct device *self, void *aux)
19426c1ffa3Skettenis {
19526c1ffa3Skettenis struct smu_softc *sc = (struct smu_softc *)self;
1961e8e22a4Skettenis struct confargs *ca = aux;
197a5a63700Sderaadt struct i2cbus_attach_args iba;
1981e8e22a4Skettenis struct smu_fan *fan;
1991e8e22a4Skettenis struct smu_sensor *sensor;
2001e8e22a4Skettenis int nseg, node;
2011e8e22a4Skettenis char type[32], loc[32];
2021e8e22a4Skettenis u_int32_t reg, intr, gpio, val;
203865897fdSderaadt #ifndef SMALL_KERNEL
2041e8e22a4Skettenis u_int8_t data[12];
205865897fdSderaadt #endif
20626c1ffa3Skettenis
20726c1ffa3Skettenis /* XXX */
20826c1ffa3Skettenis sc->sc_mem_bus_space.bus_base = 0x80000000;
20926c1ffa3Skettenis sc->sc_mem_bus_space.bus_size = 0;
21026c1ffa3Skettenis sc->sc_mem_bus_space.bus_io = 0;
21126c1ffa3Skettenis sc->sc_memt = &sc->sc_mem_bus_space;
21226c1ffa3Skettenis
2131e8e22a4Skettenis /* Map smu-doorbell gpio. */
2141e8e22a4Skettenis if (OF_getprop(ca->ca_node, "platform-doorbell-ack",
2151e8e22a4Skettenis &node, sizeof node) <= 0 ||
2161e8e22a4Skettenis OF_getprop(node, "reg", ®, sizeof reg) <= 0 ||
2171e8e22a4Skettenis OF_getprop(node, "interrupts", &intr, sizeof intr) <= 0 ||
2181e8e22a4Skettenis OF_getprop(OF_parent(node), "reg", &gpio, sizeof gpio) <= 0) {
2191e8e22a4Skettenis printf(": cannot find smu-doorbell gpio\n");
2201e8e22a4Skettenis return;
2211e8e22a4Skettenis }
2221e8e22a4Skettenis if (bus_space_map(sc->sc_memt, gpio + reg, 1, 0, &sc->sc_gpioh)) {
22326c1ffa3Skettenis printf(": cannot map smu-doorbell gpio\n");
22426c1ffa3Skettenis return;
22526c1ffa3Skettenis }
22626c1ffa3Skettenis
22726c1ffa3Skettenis /* XXX Should get this from OF. */
22826c1ffa3Skettenis if (bus_space_map(sc->sc_memt, 0x860c, 4, 0, &sc->sc_buffh)) {
22926c1ffa3Skettenis printf(": cannot map smu-doorbell buffer\n");
23026c1ffa3Skettenis return;
23126c1ffa3Skettenis }
23226c1ffa3Skettenis
23326c1ffa3Skettenis /* XXX */
23426c1ffa3Skettenis sc->sc_dmat = &pci_bus_dma_tag;
23526c1ffa3Skettenis
23626c1ffa3Skettenis /* Allocate and map SMU command buffer. */
23726c1ffa3Skettenis if (bus_dmamem_alloc(sc->sc_dmat, SMU_CMDSZ, 0, 0,
23826c1ffa3Skettenis sc->sc_cmdseg, 1, &nseg, BUS_DMA_NOWAIT)) {
23926c1ffa3Skettenis printf(": cannot allocate cmd buffer\n");
24026c1ffa3Skettenis return;
24126c1ffa3Skettenis }
24226c1ffa3Skettenis if (bus_dmamem_map(sc->sc_dmat, sc->sc_cmdseg, nseg,
24326c1ffa3Skettenis SMU_CMDSZ, &sc->sc_cmd, BUS_DMA_NOWAIT)) {
24426c1ffa3Skettenis printf(": cannot map cmd buffer\n");
24526c1ffa3Skettenis bus_dmamem_free(sc->sc_dmat, sc->sc_cmdseg, 1);
24626c1ffa3Skettenis return;
24726c1ffa3Skettenis }
24826c1ffa3Skettenis if (bus_dmamap_create(sc->sc_dmat, SMU_CMDSZ, 1, SMU_CMDSZ, 0,
24926c1ffa3Skettenis BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &sc->sc_cmdmap)) {
25026c1ffa3Skettenis printf(": cannot create cmd dmamap\n");
25126c1ffa3Skettenis bus_dmamem_unmap(sc->sc_dmat, sc->sc_cmd, SMU_CMDSZ);
25226c1ffa3Skettenis bus_dmamem_free(sc->sc_dmat, sc->sc_cmdseg, 1);
25326c1ffa3Skettenis return;
25426c1ffa3Skettenis }
25526c1ffa3Skettenis if (bus_dmamap_load(sc->sc_dmat, sc->sc_cmdmap, sc->sc_cmd,
25626c1ffa3Skettenis SMU_CMDSZ, NULL, BUS_DMA_NOWAIT)) {
25726c1ffa3Skettenis printf(": cannot load cmd dmamap\n");
25826c1ffa3Skettenis bus_dmamap_destroy(sc->sc_dmat, sc->sc_cmdmap);
25926c1ffa3Skettenis bus_dmamem_unmap(sc->sc_dmat, sc->sc_cmd, SMU_CMDSZ);
26026c1ffa3Skettenis bus_dmamem_free(sc->sc_dmat, sc->sc_cmdseg, nseg);
26126c1ffa3Skettenis return;
26226c1ffa3Skettenis }
26326c1ffa3Skettenis
264769768baSthib rw_init(&sc->sc_lock, sc->sc_dev.dv_xname);
2651e8e22a4Skettenis
2661e8e22a4Skettenis /* Establish smu-doorbell interrupt. */
2671e8e22a4Skettenis mac_intr_establish(parent, intr, IST_EDGE, IPL_BIO,
2681ac74572Sderaadt smu_intr, sc, sc->sc_dev.dv_xname);
2691e8e22a4Skettenis
27026c1ffa3Skettenis /* Initialize global variables that control RTC functionality. */
27126c1ffa3Skettenis time_read = smu_time_read;
27226c1ffa3Skettenis time_write = smu_time_write;
27326c1ffa3Skettenis
274528e43b2Smglocker /* RPM Fans */
275ded34fa7Skettenis node = OF_getnodebyname(ca->ca_node, "rpm-fans");
27610fd3003Skettenis if (node == 0)
27710fd3003Skettenis node = OF_getnodebyname(ca->ca_node, "fans");
2781e8e22a4Skettenis for (node = OF_child(node); node; node = OF_peer(node)) {
2791e8e22a4Skettenis if (OF_getprop(node, "reg", ®, sizeof reg) <= 0 ||
2801e8e22a4Skettenis OF_getprop(node, "device_type", type, sizeof type) <= 0)
2811e8e22a4Skettenis continue;
2821e8e22a4Skettenis
2831e8e22a4Skettenis if (strcmp(type, "fan-rpm-control") != 0) {
284528e43b2Smglocker printf(": unsupported rpm-fan type: %s\n", type);
2851e8e22a4Skettenis return;
2861e8e22a4Skettenis }
2871e8e22a4Skettenis
2881e8e22a4Skettenis if (sc->sc_num_fans >= SMU_MAXFANS) {
2891e8e22a4Skettenis printf(": too many fans\n");
2901e8e22a4Skettenis return;
2911e8e22a4Skettenis }
2921e8e22a4Skettenis
2931e8e22a4Skettenis fan = &sc->sc_fans[sc->sc_num_fans++];
2941e8e22a4Skettenis fan->sensor.type = SENSOR_FANRPM;
2951e8e22a4Skettenis fan->sensor.flags = SENSOR_FINVALID;
2961e8e22a4Skettenis fan->reg = reg;
2971e8e22a4Skettenis
2981e8e22a4Skettenis if (OF_getprop(node, "min-value", &val, sizeof val) <= 0)
2991e8e22a4Skettenis val = 0;
3001e8e22a4Skettenis fan->min_rpm = val;
3011e8e22a4Skettenis if (OF_getprop(node, "max-value", &val, sizeof val) <= 0)
3021e8e22a4Skettenis val = 0xffff;
3031e8e22a4Skettenis fan->max_rpm = val;
304d01d0224Smpi if (OF_getprop(node, "unmanage-value", &val, sizeof val) > 0)
3051e8e22a4Skettenis fan->unmanaged_rpm = val;
306d01d0224Smpi else if (OF_getprop(node, "safe-value", &val, sizeof val) > 0)
307d01d0224Smpi fan->unmanaged_rpm = val;
308d01d0224Smpi else
309d01d0224Smpi fan->unmanaged_rpm = fan->max_rpm;
3101e8e22a4Skettenis
3111e8e22a4Skettenis if (OF_getprop(node, "location", loc, sizeof loc) <= 0)
3121e8e22a4Skettenis strlcpy(loc, "Unknown", sizeof loc);
3131e8e22a4Skettenis strlcpy(fan->sensor.desc, loc, sizeof sensor->sensor.desc);
3141e8e22a4Skettenis
3151e8e22a4Skettenis /* Start running fans at their "unmanaged" speed. */
3161e8e22a4Skettenis smu_fan_set_rpm(sc, fan, fan->unmanaged_rpm);
3171e8e22a4Skettenis
318309d5690Smglocker /* Register fan at thermal management framework. */
319309d5690Smglocker fan->fan.min_rpm = fan->min_rpm;
320309d5690Smglocker fan->fan.max_rpm = fan->max_rpm;
321309d5690Smglocker fan->fan.default_rpm = fan->unmanaged_rpm;
322309d5690Smglocker strlcpy(fan->fan.name, loc, sizeof fan->fan.name);
323309d5690Smglocker OF_getprop(node, "zone", &fan->fan.zone, sizeof fan->fan.zone);
324309d5690Smglocker fan->fan.set = (int (*)(struct thermal_fan *, int))
325309d5690Smglocker smu_fan_set_rpm_thermal;
326309d5690Smglocker thermal_fan_register(&fan->fan);
327865897fdSderaadt #ifndef SMALL_KERNEL
32827515a6bSderaadt sensor_attach(&sc->sc_sensordev, &fan->sensor);
329865897fdSderaadt #endif
3301e8e22a4Skettenis }
3311e8e22a4Skettenis
332528e43b2Smglocker /* PWM Fans */
333528e43b2Smglocker node = OF_getnodebyname(ca->ca_node, "pwm-fans");
334528e43b2Smglocker for (node = OF_child(node); node; node = OF_peer(node)) {
335528e43b2Smglocker if (OF_getprop(node, "reg", ®, sizeof reg) <= 0 ||
336528e43b2Smglocker OF_getprop(node, "device_type", type, sizeof type) <= 0)
337528e43b2Smglocker continue;
338528e43b2Smglocker
339528e43b2Smglocker if (strcmp(type, "fan-pwm-control") != 0) {
340528e43b2Smglocker printf(": unsupported pwm-fan type: %s\n", type);
341528e43b2Smglocker return;
342528e43b2Smglocker }
343528e43b2Smglocker
344528e43b2Smglocker if (sc->sc_num_fans >= SMU_MAXFANS) {
345528e43b2Smglocker printf(": too many fans\n");
346528e43b2Smglocker return;
347528e43b2Smglocker }
348528e43b2Smglocker
349528e43b2Smglocker fan = &sc->sc_fans[sc->sc_num_fans++];
350528e43b2Smglocker fan->sensor.type = SENSOR_PERCENT;
351528e43b2Smglocker fan->sensor.flags = SENSOR_FINVALID;
352528e43b2Smglocker fan->reg = reg;
353528e43b2Smglocker
354528e43b2Smglocker if (OF_getprop(node, "min-value", &val, sizeof val) <= 0)
355528e43b2Smglocker val = 0;
356528e43b2Smglocker fan->min_pwm = val;
357528e43b2Smglocker if (OF_getprop(node, "max-value", &val, sizeof val) <= 0)
358528e43b2Smglocker val = 0xffff;
359528e43b2Smglocker fan->max_pwm = val;
360528e43b2Smglocker if (OF_getprop(node, "unmanage-value", &val, sizeof val) > 0)
361528e43b2Smglocker fan->unmanaged_pwm = val;
362528e43b2Smglocker else if (OF_getprop(node, "safe-value", &val, sizeof val) > 0)
363528e43b2Smglocker fan->unmanaged_pwm = val;
364528e43b2Smglocker else
365528e43b2Smglocker fan->unmanaged_pwm = fan->min_pwm;
366528e43b2Smglocker
367528e43b2Smglocker if (OF_getprop(node, "location", loc, sizeof loc) <= 0)
368528e43b2Smglocker strlcpy(loc, "Unknown", sizeof loc);
369528e43b2Smglocker strlcpy(fan->sensor.desc, loc, sizeof sensor->sensor.desc);
370528e43b2Smglocker
371528e43b2Smglocker /* Start running fans at their "unmanaged" speed. */
372528e43b2Smglocker smu_fan_set_pwm(sc, fan, fan->unmanaged_pwm);
373528e43b2Smglocker
374309d5690Smglocker /* Register fan at thermal management framework. */
375309d5690Smglocker fan->fan.min_rpm = fan->min_pwm;
376309d5690Smglocker fan->fan.max_rpm = fan->max_pwm;
377309d5690Smglocker fan->fan.default_rpm = fan->unmanaged_pwm;
378309d5690Smglocker strlcpy(fan->fan.name, loc, sizeof fan->fan.name);
379309d5690Smglocker OF_getprop(node, "zone", &fan->fan.zone, sizeof fan->fan.zone);
380309d5690Smglocker fan->fan.set = (int (*)(struct thermal_fan *, int))
381309d5690Smglocker smu_fan_set_pwm_thermal;
382309d5690Smglocker thermal_fan_register(&fan->fan);
383528e43b2Smglocker #ifndef SMALL_KERNEL
384528e43b2Smglocker sensor_attach(&sc->sc_sensordev, &fan->sensor);
385528e43b2Smglocker #endif
386528e43b2Smglocker }
387528e43b2Smglocker
388cb2ccc16Skettenis /*
389cb2ccc16Skettenis * Bail out if we didn't find any fans. If we don't set the
390cb2ccc16Skettenis * fans to a safe speed, but tickle the SMU periodically by
391cb2ccc16Skettenis * reading sensors, the fans will never spin up and the
392cb2ccc16Skettenis * machine might overheat.
393cb2ccc16Skettenis */
394cb2ccc16Skettenis if (sc->sc_num_fans == 0) {
395cb2ccc16Skettenis printf(": no fans\n");
396cb2ccc16Skettenis return;
397cb2ccc16Skettenis }
398cb2ccc16Skettenis
399d5fb2debSmglocker /* Probe the smu firmware version */
400d5fb2debSmglocker smu_firmware_probe(sc, &sc->sc_fans[0]);
401d5fb2debSmglocker
402865897fdSderaadt #ifndef SMALL_KERNEL
4031e8e22a4Skettenis /* Sensors */
4041e8e22a4Skettenis node = OF_getnodebyname(ca->ca_node, "sensors");
4051e8e22a4Skettenis for (node = OF_child(node); node; node = OF_peer(node)) {
4061e8e22a4Skettenis if (OF_getprop(node, "reg", &val, sizeof val) <= 0 ||
4071e8e22a4Skettenis OF_getprop(node, "device_type", type, sizeof type) <= 0)
4081e8e22a4Skettenis continue;
4091e8e22a4Skettenis
410e242de35Skettenis if (sc->sc_num_sensors >= SMU_MAXSENSORS) {
411e242de35Skettenis printf(": too many sensors\n");
412e242de35Skettenis return;
413e242de35Skettenis }
414e242de35Skettenis
4151e8e22a4Skettenis sensor = &sc->sc_sensors[sc->sc_num_sensors++];
4161e8e22a4Skettenis sensor->sensor.flags = SENSOR_FINVALID;
4171e8e22a4Skettenis sensor->reg = val;
4181e8e22a4Skettenis
4191e8e22a4Skettenis if (strcmp(type, "current-sensor") == 0) {
4201e8e22a4Skettenis sensor->sensor.type = SENSOR_AMPS;
4211e8e22a4Skettenis } else if (strcmp(type, "temp-sensor") == 0) {
4221e8e22a4Skettenis sensor->sensor.type = SENSOR_TEMP;
4231e8e22a4Skettenis } else if (strcmp(type, "voltage-sensor") == 0) {
4241e8e22a4Skettenis sensor->sensor.type = SENSOR_VOLTS_DC;
4250367fc9dSkettenis } else if (strcmp(type, "power-sensor") == 0) {
4260367fc9dSkettenis sensor->sensor.type = SENSOR_WATTS;
4271e8e22a4Skettenis } else {
4281e8e22a4Skettenis sensor->sensor.type = SENSOR_INTEGER;
4291e8e22a4Skettenis }
4301e8e22a4Skettenis
4311e8e22a4Skettenis if (OF_getprop(node, "location", loc, sizeof loc) <= 0)
4321e8e22a4Skettenis strlcpy(loc, "Unknown", sizeof loc);
4331e8e22a4Skettenis strlcpy(sensor->sensor.desc, loc, sizeof sensor->sensor.desc);
4341e8e22a4Skettenis
435309d5690Smglocker /* Register temp. sensor at thermal management framework. */
436309d5690Smglocker if (sensor->sensor.type == SENSOR_TEMP) {
437309d5690Smglocker sensor->therm.target_temp = TEMP_TRG;
438309d5690Smglocker sensor->therm.max_temp = TEMP_MAX;
439309d5690Smglocker strlcpy(sensor->therm.name, loc,
440309d5690Smglocker sizeof sensor->therm.name);
441309d5690Smglocker OF_getprop(node, "zone", &sensor->therm.zone,
442309d5690Smglocker sizeof sensor->therm.zone);
443309d5690Smglocker sensor->therm.read = (int (*)
444309d5690Smglocker (struct thermal_temp *))smu_sensor_refresh_thermal;
445309d5690Smglocker thermal_sensor_register(&sensor->therm);
446309d5690Smglocker }
447309d5690Smglocker
44827515a6bSderaadt sensor_attach(&sc->sc_sensordev, &sensor->sensor);
4491e8e22a4Skettenis }
4501e8e22a4Skettenis
45127515a6bSderaadt /* Register sensor device with sysctl */
45227515a6bSderaadt strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
45327515a6bSderaadt sizeof(sc->sc_sensordev.xname));
45427515a6bSderaadt sensordev_install(&sc->sc_sensordev);
45527515a6bSderaadt
4561e8e22a4Skettenis /* CPU temperature diode calibration */
4571e8e22a4Skettenis smu_get_datablock(sc, 0x18, data, sizeof data);
4581e8e22a4Skettenis sc->sc_cpu_diode_scale = (data[4] << 8) + data[5];
4591e8e22a4Skettenis sc->sc_cpu_diode_offset = (data[6] << 8) + data[7];
4601e8e22a4Skettenis
4611e8e22a4Skettenis /* CPU power (voltage and current) calibration */
4621e8e22a4Skettenis smu_get_datablock(sc, 0x21, data, sizeof data);
4631e8e22a4Skettenis sc->sc_cpu_volt_scale = (data[4] << 8) + data[5];
4641e8e22a4Skettenis sc->sc_cpu_volt_offset = (data[6] << 8) + data[7];
4651e8e22a4Skettenis sc->sc_cpu_curr_scale = (data[8] << 8) + data[9];
4661e8e22a4Skettenis sc->sc_cpu_curr_offset = (data[10] << 8) + data[11];
4671e8e22a4Skettenis
4680367fc9dSkettenis /* Slots power calibration */
4690367fc9dSkettenis smu_get_datablock(sc, 0x78, data, sizeof data);
4700367fc9dSkettenis sc->sc_slots_pow_scale = (data[4] << 8) + data[5];
4710367fc9dSkettenis sc->sc_slots_pow_offset = (data[6] << 8) + data[7];
4720367fc9dSkettenis
473aa803ae9Skettenis sensor_task_register(sc, smu_refresh_sensors, 5);
474865897fdSderaadt #endif /* !SMALL_KERNEL */
47526c1ffa3Skettenis printf("\n");
4762e975dd8Skettenis
477b6032f37Skettenis ppc64_slew_voltage = smu_slew_voltage;
478b6032f37Skettenis
4792e975dd8Skettenis sc->sc_i2c_tag.ic_cookie = sc;
4802e975dd8Skettenis sc->sc_i2c_tag.ic_acquire_bus = smu_i2c_acquire_bus;
4812e975dd8Skettenis sc->sc_i2c_tag.ic_release_bus = smu_i2c_release_bus;
4822e975dd8Skettenis sc->sc_i2c_tag.ic_exec = smu_i2c_exec;
4832e975dd8Skettenis
484ca764d7fSkettenis /*
485ca764d7fSkettenis * Early versions of the SMU have the i2c bus node directly
486ca764d7fSkettenis * below the "smu" node, while later models have an
487ca764d7fSkettenis * intermediate "smu-i2c-control" node.
488ca764d7fSkettenis */
4892e975dd8Skettenis node = OF_getnodebyname(ca->ca_node, "smu-i2c-control");
490ca764d7fSkettenis if (node)
491a5a63700Sderaadt node = OF_child(node);
492ca764d7fSkettenis else
493ca764d7fSkettenis node = OF_getnodebyname(ca->ca_node, "i2c");
4942e975dd8Skettenis
495a5a63700Sderaadt bzero(&iba, sizeof iba);
496a5a63700Sderaadt iba.iba_name = "iic";
4972e975dd8Skettenis iba.iba_tag = &sc->sc_i2c_tag;
498a5a63700Sderaadt iba.iba_bus_scan = maciic_scan;
499a5a63700Sderaadt iba.iba_bus_scan_arg = &node;
5002e975dd8Skettenis config_found(&sc->sc_dev, &iba, NULL);
50126c1ffa3Skettenis }
50226c1ffa3Skettenis
5031e8e22a4Skettenis int
smu_intr(void * arg)5041e8e22a4Skettenis smu_intr(void *arg)
5051e8e22a4Skettenis {
5061e8e22a4Skettenis wakeup(arg);
5071e8e22a4Skettenis return 1;
5081e8e22a4Skettenis }
5091e8e22a4Skettenis
51026c1ffa3Skettenis int
smu_do_cmd(struct smu_softc * sc,int msecs)5113bf93282Scheloha smu_do_cmd(struct smu_softc *sc, int msecs)
51226c1ffa3Skettenis {
51326c1ffa3Skettenis struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd;
51426c1ffa3Skettenis u_int8_t gpio, ack = ~cmd->cmd;
5151e8e22a4Skettenis int error;
51626c1ffa3Skettenis
51726c1ffa3Skettenis /* Write to mailbox. */
51826c1ffa3Skettenis bus_space_write_4(sc->sc_memt, sc->sc_buffh, 0,
51926c1ffa3Skettenis sc->sc_cmdmap->dm_segs->ds_addr);
52026c1ffa3Skettenis
52126c1ffa3Skettenis /* Flush to RAM. */
5222df76cc2Sguenther asm volatile ("dcbst 0,%0; sync" :: "r"(sc->sc_cmd): "memory");
52326c1ffa3Skettenis
52426c1ffa3Skettenis /* Ring doorbell. */
52526c1ffa3Skettenis bus_space_write_1(sc->sc_memt, sc->sc_gpioh, 0, GPIO_DDR_OUTPUT);
5261e8e22a4Skettenis
52726c1ffa3Skettenis do {
5283bf93282Scheloha error = tsleep_nsec(sc, PWAIT, "smu", MSEC_TO_NSEC(msecs));
5291e8e22a4Skettenis if (error)
5301e8e22a4Skettenis return (error);
53126c1ffa3Skettenis gpio = bus_space_read_1(sc->sc_memt, sc->sc_gpioh, 0);
53226c1ffa3Skettenis } while (!(gpio & (GPIO_DATA)));
53326c1ffa3Skettenis
53426c1ffa3Skettenis /* CPU might have brought back the cache line. */
5352df76cc2Sguenther asm volatile ("dcbf 0,%0; sync" :: "r"(sc->sc_cmd) : "memory");
53626c1ffa3Skettenis
53726c1ffa3Skettenis if (cmd->cmd != ack)
53826c1ffa3Skettenis return (EIO);
53926c1ffa3Skettenis return (0);
54026c1ffa3Skettenis }
54126c1ffa3Skettenis
54226c1ffa3Skettenis int
smu_time_read(time_t * secs)54326c1ffa3Skettenis smu_time_read(time_t *secs)
54426c1ffa3Skettenis {
54526c1ffa3Skettenis struct smu_softc *sc = smu_cd.cd_devs[0];
54626c1ffa3Skettenis struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd;
54726c1ffa3Skettenis struct clock_ymdhms dt;
54826c1ffa3Skettenis int error;
54926c1ffa3Skettenis
550769768baSthib rw_enter_write(&sc->sc_lock);
5511e8e22a4Skettenis
55226c1ffa3Skettenis cmd->cmd = SMU_RTC;
55326c1ffa3Skettenis cmd->len = 1;
55426c1ffa3Skettenis cmd->data[0] = SMU_RTC_GET_DATETIME;
55526c1ffa3Skettenis error = smu_do_cmd(sc, 800);
55626c1ffa3Skettenis if (error) {
557769768baSthib rw_exit_write(&sc->sc_lock);
558d9e828b2Skettenis
55926c1ffa3Skettenis *secs = 0;
56026c1ffa3Skettenis return (error);
56126c1ffa3Skettenis }
56226c1ffa3Skettenis
56326c1ffa3Skettenis dt.dt_year = 2000 + FROMBCD(cmd->data[6]);
56426c1ffa3Skettenis dt.dt_mon = FROMBCD(cmd->data[5]);
56526c1ffa3Skettenis dt.dt_day = FROMBCD(cmd->data[4]);
56626c1ffa3Skettenis dt.dt_hour = FROMBCD(cmd->data[2]);
56726c1ffa3Skettenis dt.dt_min = FROMBCD(cmd->data[1]);
56826c1ffa3Skettenis dt.dt_sec = FROMBCD(cmd->data[0]);
5691e8e22a4Skettenis
570769768baSthib rw_exit_write(&sc->sc_lock);
5711e8e22a4Skettenis
57226c1ffa3Skettenis *secs = clock_ymdhms_to_secs(&dt);
57326c1ffa3Skettenis return (0);
57426c1ffa3Skettenis }
57526c1ffa3Skettenis
57626c1ffa3Skettenis int
smu_time_write(time_t secs)57726c1ffa3Skettenis smu_time_write(time_t secs)
57826c1ffa3Skettenis {
57926c1ffa3Skettenis struct smu_softc *sc = smu_cd.cd_devs[0];
58026c1ffa3Skettenis struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd;
58126c1ffa3Skettenis struct clock_ymdhms dt;
5821e8e22a4Skettenis int error;
58326c1ffa3Skettenis
58426c1ffa3Skettenis clock_secs_to_ymdhms(secs, &dt);
58526c1ffa3Skettenis
586769768baSthib rw_enter_write(&sc->sc_lock);
5871e8e22a4Skettenis
58826c1ffa3Skettenis cmd->cmd = SMU_RTC;
58926c1ffa3Skettenis cmd->len = 8;
59026c1ffa3Skettenis cmd->data[0] = SMU_RTC_SET_DATETIME;
59126c1ffa3Skettenis cmd->data[1] = TOBCD(dt.dt_sec);
59226c1ffa3Skettenis cmd->data[2] = TOBCD(dt.dt_min);
59326c1ffa3Skettenis cmd->data[3] = TOBCD(dt.dt_hour);
59426c1ffa3Skettenis cmd->data[4] = TOBCD(dt.dt_wday);
59526c1ffa3Skettenis cmd->data[5] = TOBCD(dt.dt_day);
59626c1ffa3Skettenis cmd->data[6] = TOBCD(dt.dt_mon);
59726c1ffa3Skettenis cmd->data[7] = TOBCD(dt.dt_year - 2000);
5981e8e22a4Skettenis error = smu_do_cmd(sc, 800);
59926c1ffa3Skettenis
600769768baSthib rw_exit_write(&sc->sc_lock);
6011e8e22a4Skettenis
6021e8e22a4Skettenis return (error);
6031e8e22a4Skettenis }
6041e8e22a4Skettenis
6051e8e22a4Skettenis
6061e8e22a4Skettenis int
smu_get_datablock(struct smu_softc * sc,u_int8_t id,u_int8_t * buf,size_t len)6071e8e22a4Skettenis smu_get_datablock(struct smu_softc *sc, u_int8_t id, u_int8_t *buf, size_t len)
6081e8e22a4Skettenis {
6091e8e22a4Skettenis struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd;
6101e8e22a4Skettenis u_int8_t addr[4];
6111e8e22a4Skettenis int error;
6121e8e22a4Skettenis
6131e8e22a4Skettenis cmd->cmd = SMU_PARTITION;
6141e8e22a4Skettenis cmd->len = 2;
6151e8e22a4Skettenis cmd->data[0] = SMU_PARTITION_LATEST;
6161e8e22a4Skettenis cmd->data[1] = id;
6171e8e22a4Skettenis error = smu_do_cmd(sc, 800);
6181e8e22a4Skettenis if (error)
6191e8e22a4Skettenis return (error);
6201e8e22a4Skettenis
6211e8e22a4Skettenis addr[0] = 0x00;
6221e8e22a4Skettenis addr[1] = 0x00;
6231e8e22a4Skettenis addr[2] = cmd->data[0];
6241e8e22a4Skettenis addr[3] = cmd->data[1];
6251e8e22a4Skettenis
6261e8e22a4Skettenis cmd->cmd = SMU_MISC;
6271e8e22a4Skettenis cmd->len = 7;
6281e8e22a4Skettenis cmd->data[0] = SMU_MISC_GET_DATA;
6291e8e22a4Skettenis cmd->data[1] = sizeof(u_int32_t);
6301e8e22a4Skettenis cmd->data[2] = addr[0];
6311e8e22a4Skettenis cmd->data[3] = addr[1];
6321e8e22a4Skettenis cmd->data[4] = addr[2];
6331e8e22a4Skettenis cmd->data[5] = addr[3];
6341e8e22a4Skettenis cmd->data[6] = len;
6351e8e22a4Skettenis error = smu_do_cmd(sc, 800);
6361e8e22a4Skettenis if (error)
6371e8e22a4Skettenis return (error);
6381e8e22a4Skettenis
6391e8e22a4Skettenis memcpy(buf, cmd->data, len);
6401e8e22a4Skettenis return (0);
6411e8e22a4Skettenis }
6421e8e22a4Skettenis
643d5fb2debSmglocker void
smu_firmware_probe(struct smu_softc * sc,struct smu_fan * fan)644d5fb2debSmglocker smu_firmware_probe(struct smu_softc *sc, struct smu_fan *fan)
645d5fb2debSmglocker {
646d5fb2debSmglocker struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd;
647d5fb2debSmglocker int error;
648d5fb2debSmglocker
649d5fb2debSmglocker /*
650d5fb2debSmglocker * Find out if the smu runs an old or new firmware version
651d5fb2debSmglocker * by sending a new firmware command to read the fan speed.
652d5fb2debSmglocker * If it fails we assume to have an old firmware version.
653d5fb2debSmglocker */
654d5fb2debSmglocker cmd->cmd = SMU_FAN;
655d5fb2debSmglocker cmd->len = 2;
656d5fb2debSmglocker cmd->data[0] = 0x31;
657d5fb2debSmglocker cmd->data[1] = fan->reg;
658d5fb2debSmglocker error = smu_do_cmd(sc, 800);
659d5fb2debSmglocker if (error)
660d5fb2debSmglocker sc->sc_firmware_old = 1;
661d5fb2debSmglocker }
662d5fb2debSmglocker
6631e8e22a4Skettenis int
smu_fan_set_rpm(struct smu_softc * sc,struct smu_fan * fan,u_int16_t rpm)6641e8e22a4Skettenis smu_fan_set_rpm(struct smu_softc *sc, struct smu_fan *fan, u_int16_t rpm)
6651e8e22a4Skettenis {
6661e8e22a4Skettenis struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd;
6671e8e22a4Skettenis
66810fd3003Skettenis /*
66910fd3003Skettenis * On the PowerMac8,2 this command expects the requested fan
67010fd3003Skettenis * speed at a different location in the command block than on
67110fd3003Skettenis * the PowerMac8,1. We simply store the value at both
67210fd3003Skettenis * locations.
67310fd3003Skettenis */
674d5fb2debSmglocker if (sc->sc_firmware_old) {
6751e8e22a4Skettenis cmd->cmd = SMU_FAN;
67610fd3003Skettenis cmd->len = 14;
6771e8e22a4Skettenis cmd->data[0] = 0x00; /* fan-rpm-control */
6781e8e22a4Skettenis cmd->data[1] = 0x01 << fan->reg;
67910fd3003Skettenis cmd->data[2] = cmd->data[2 + fan->reg * 2] = (rpm >> 8) & 0xff;
68010fd3003Skettenis cmd->data[3] = cmd->data[3 + fan->reg * 2] = (rpm & 0xff);
681d5fb2debSmglocker } else {
682d5fb2debSmglocker cmd->cmd = SMU_FAN;
683d5fb2debSmglocker cmd->len = 4;
684d5fb2debSmglocker cmd->data[0] = 0x30;
685d5fb2debSmglocker cmd->data[1] = fan->reg;
686d5fb2debSmglocker cmd->data[2] = (rpm >> 8) & 0xff;
687d5fb2debSmglocker cmd->data[3] = rpm & 0xff;
688d5fb2debSmglocker }
6891e8e22a4Skettenis return smu_do_cmd(sc, 800);
6901e8e22a4Skettenis }
6911e8e22a4Skettenis
6921e8e22a4Skettenis int
smu_fan_set_pwm(struct smu_softc * sc,struct smu_fan * fan,u_int16_t pwm)693528e43b2Smglocker smu_fan_set_pwm(struct smu_softc *sc, struct smu_fan *fan, u_int16_t pwm)
694528e43b2Smglocker {
695528e43b2Smglocker struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd;
696528e43b2Smglocker
697d5fb2debSmglocker if (sc->sc_firmware_old) {
698528e43b2Smglocker cmd->cmd = SMU_FAN;
699528e43b2Smglocker cmd->len = 14;
700528e43b2Smglocker cmd->data[0] = 0x10; /* fan-pwm-control */
701528e43b2Smglocker cmd->data[1] = 0x01 << fan->reg;
702528e43b2Smglocker cmd->data[2] = cmd->data[2 + fan->reg * 2] = (pwm >> 8) & 0xff;
703528e43b2Smglocker cmd->data[3] = cmd->data[3 + fan->reg * 2] = (pwm & 0xff);
704d5fb2debSmglocker } else {
705d5fb2debSmglocker cmd->cmd = SMU_FAN;
706d5fb2debSmglocker cmd->len = 4;
707d5fb2debSmglocker cmd->data[0] = 0x30;
708d5fb2debSmglocker cmd->data[1] = fan->reg;
709d5fb2debSmglocker cmd->data[2] = (pwm >> 8) & 0xff;
710d5fb2debSmglocker cmd->data[3] = pwm & 0xff;
711d5fb2debSmglocker }
712528e43b2Smglocker return smu_do_cmd(sc, 800);
713528e43b2Smglocker }
714528e43b2Smglocker
715528e43b2Smglocker int
smu_fan_read_rpm(struct smu_softc * sc,struct smu_fan * fan,u_int16_t * rpm)716a16ae0caSmglocker smu_fan_read_rpm(struct smu_softc *sc, struct smu_fan *fan, u_int16_t *rpm)
717a16ae0caSmglocker {
718a16ae0caSmglocker struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd;
719a16ae0caSmglocker int error;
720a16ae0caSmglocker
721d5fb2debSmglocker if (sc->sc_firmware_old) {
722a16ae0caSmglocker cmd->cmd = SMU_FAN;
723a16ae0caSmglocker cmd->len = 1;
724a16ae0caSmglocker cmd->data[0] = 0x01; /* fan-rpm-control */
725a16ae0caSmglocker error = smu_do_cmd(sc, 800);
726a16ae0caSmglocker if (error)
727a16ae0caSmglocker return (error);
728d5fb2debSmglocker *rpm = (cmd->data[fan->reg * 2 + 1] << 8) |
729d5fb2debSmglocker cmd->data[fan->reg * 2 + 2];
730d5fb2debSmglocker } else {
731d5fb2debSmglocker cmd->cmd = SMU_FAN;
732d5fb2debSmglocker cmd->len = 2;
733d5fb2debSmglocker cmd->data[0] = 0x31;
734d5fb2debSmglocker cmd->data[1] = fan->reg;
735d5fb2debSmglocker error = smu_do_cmd(sc, 800);
736d5fb2debSmglocker if (error)
737d5fb2debSmglocker return (error);
738d5fb2debSmglocker *rpm = (cmd->data[0] << 8) | cmd->data[1];
739d5fb2debSmglocker }
740a16ae0caSmglocker
741a16ae0caSmglocker return (0);
742a16ae0caSmglocker }
743a16ae0caSmglocker
744a16ae0caSmglocker int
smu_fan_read_pwm(struct smu_softc * sc,struct smu_fan * fan,u_int16_t * pwm,u_int16_t * rpm)745528e43b2Smglocker smu_fan_read_pwm(struct smu_softc *sc, struct smu_fan *fan, u_int16_t *pwm,
746528e43b2Smglocker u_int16_t *rpm)
747528e43b2Smglocker {
748528e43b2Smglocker struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd;
749528e43b2Smglocker int error;
750528e43b2Smglocker
751d5fb2debSmglocker if (sc->sc_firmware_old) {
752528e43b2Smglocker /* read PWM value */
753528e43b2Smglocker cmd->cmd = SMU_FAN;
754528e43b2Smglocker cmd->len = 14;
755528e43b2Smglocker cmd->data[0] = 0x12;
756528e43b2Smglocker cmd->data[1] = 0x01 << fan->reg;
757528e43b2Smglocker error = smu_do_cmd(sc, 800);
758528e43b2Smglocker if (error)
759528e43b2Smglocker return (error);
760528e43b2Smglocker *pwm = cmd->data[fan->reg * 2 + 2];
761528e43b2Smglocker
762528e43b2Smglocker /* read RPM value */
763528e43b2Smglocker cmd->cmd = SMU_FAN;
764528e43b2Smglocker cmd->len = 1;
765528e43b2Smglocker cmd->data[0] = 0x11;
766528e43b2Smglocker error = smu_do_cmd(sc, 800);
767528e43b2Smglocker if (error)
768528e43b2Smglocker return (error);
769d5fb2debSmglocker *rpm = (cmd->data[fan->reg * 2 + 1] << 8) |
770d5fb2debSmglocker cmd->data[fan->reg * 2 + 2];
771d5fb2debSmglocker } else {
772d5fb2debSmglocker cmd->cmd = SMU_FAN;
773d5fb2debSmglocker cmd->len = 2;
774d5fb2debSmglocker cmd->data[0] = 0x31;
775d5fb2debSmglocker cmd->data[1] = fan->reg;
776d5fb2debSmglocker error = smu_do_cmd(sc, 800);
777d5fb2debSmglocker if (error)
778d5fb2debSmglocker return (error);
779d5fb2debSmglocker *rpm = (cmd->data[0] << 8) | cmd->data[1];
780d5fb2debSmglocker
781d5fb2debSmglocker /* XXX
782d5fb2debSmglocker * We don't know currently if there is a pwm read command
783d5fb2debSmglocker * for the new firmware as well. Therefore lets calculate
784d5fb2debSmglocker * the pwm value for now based on the rpm.
785d5fb2debSmglocker */
786d5fb2debSmglocker *pwm = *rpm * 100 / fan->max_rpm;
787d5fb2debSmglocker }
788528e43b2Smglocker
789528e43b2Smglocker return (0);
790528e43b2Smglocker }
791528e43b2Smglocker
792528e43b2Smglocker int
smu_fan_refresh(struct smu_softc * sc,struct smu_fan * fan)7931e8e22a4Skettenis smu_fan_refresh(struct smu_softc *sc, struct smu_fan *fan)
7941e8e22a4Skettenis {
7951e8e22a4Skettenis int error;
796528e43b2Smglocker u_int16_t rpm, pwm;
797528e43b2Smglocker
798528e43b2Smglocker if (fan->sensor.type == SENSOR_PERCENT) {
799528e43b2Smglocker error = smu_fan_read_pwm(sc, fan, &pwm, &rpm);
800528e43b2Smglocker if (error) {
801528e43b2Smglocker fan->sensor.flags = SENSOR_FINVALID;
802528e43b2Smglocker return (error);
803528e43b2Smglocker }
804528e43b2Smglocker fan->sensor.value = pwm * 1000;
805528e43b2Smglocker fan->sensor.flags = 0;
806a16ae0caSmglocker } else {
807a16ae0caSmglocker error = smu_fan_read_rpm(sc, fan, &rpm);
8081e8e22a4Skettenis if (error) {
8091e8e22a4Skettenis fan->sensor.flags = SENSOR_FINVALID;
8101e8e22a4Skettenis return (error);
8111e8e22a4Skettenis }
812a16ae0caSmglocker fan->sensor.value = rpm;
8131e8e22a4Skettenis fan->sensor.flags = 0;
814a16ae0caSmglocker }
815a16ae0caSmglocker
8161e8e22a4Skettenis return (0);
8171e8e22a4Skettenis }
8181e8e22a4Skettenis
8191e8e22a4Skettenis int
smu_sensor_refresh(struct smu_softc * sc,struct smu_sensor * sensor,int update_sysctl)820309d5690Smglocker smu_sensor_refresh(struct smu_softc *sc, struct smu_sensor *sensor,
821309d5690Smglocker int update_sysctl)
8221e8e22a4Skettenis {
8231e8e22a4Skettenis struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd;
8241e8e22a4Skettenis int64_t value;
8251e8e22a4Skettenis int error;
8261e8e22a4Skettenis
8271e8e22a4Skettenis cmd->cmd = SMU_ADC;
8281e8e22a4Skettenis cmd->len = 1;
8291e8e22a4Skettenis cmd->data[0] = sensor->reg;
8301e8e22a4Skettenis error = smu_do_cmd(sc, 800);
8311e8e22a4Skettenis if (error) {
8321e8e22a4Skettenis sensor->sensor.flags = SENSOR_FINVALID;
8331e8e22a4Skettenis return (error);
8341e8e22a4Skettenis }
8351e8e22a4Skettenis value = (cmd->data[0] << 8) + cmd->data[1];
836d783d3b4Sdlg switch (sensor->sensor.type) {
837d783d3b4Sdlg case SENSOR_TEMP:
8381e8e22a4Skettenis value *= sc->sc_cpu_diode_scale;
8391e8e22a4Skettenis value >>= 3;
8401e8e22a4Skettenis value += ((int64_t)sc->sc_cpu_diode_offset) << 9;
8411e8e22a4Skettenis value <<= 1;
8421e8e22a4Skettenis
8431e8e22a4Skettenis /* Convert from 16.16 fixed point degC into muK. */
8441e8e22a4Skettenis value *= 15625;
8451e8e22a4Skettenis value /= 1024;
8461e8e22a4Skettenis value += 273150000;
847d783d3b4Sdlg break;
848d783d3b4Sdlg
849d783d3b4Sdlg case SENSOR_VOLTS_DC:
8501e8e22a4Skettenis value *= sc->sc_cpu_volt_scale;
8511e8e22a4Skettenis value += sc->sc_cpu_volt_offset;
8521e8e22a4Skettenis value <<= 4;
8531e8e22a4Skettenis
8541e8e22a4Skettenis /* Convert from 16.16 fixed point V into muV. */
8551e8e22a4Skettenis value *= 15625;
8561e8e22a4Skettenis value /= 1024;
857d783d3b4Sdlg break;
858d783d3b4Sdlg
859d783d3b4Sdlg case SENSOR_AMPS:
8601e8e22a4Skettenis value *= sc->sc_cpu_curr_scale;
8611e8e22a4Skettenis value += sc->sc_cpu_curr_offset;
8621e8e22a4Skettenis value <<= 4;
8631e8e22a4Skettenis
8641e8e22a4Skettenis /* Convert from 16.16 fixed point A into muA. */
8651e8e22a4Skettenis value *= 15625;
8661e8e22a4Skettenis value /= 1024;
867d783d3b4Sdlg break;
868d783d3b4Sdlg
8690367fc9dSkettenis case SENSOR_WATTS:
8700367fc9dSkettenis value *= sc->sc_slots_pow_scale;
8710367fc9dSkettenis value += sc->sc_slots_pow_offset;
8720367fc9dSkettenis value <<= 4;
8730367fc9dSkettenis
8740367fc9dSkettenis /* Convert from 16.16 fixed point W into muW. */
8750367fc9dSkettenis value *= 15625;
8760367fc9dSkettenis value /= 1024;
8770367fc9dSkettenis break;
8780367fc9dSkettenis
879d783d3b4Sdlg default:
880d783d3b4Sdlg break;
8811e8e22a4Skettenis }
882309d5690Smglocker if (update_sysctl) {
8831e8e22a4Skettenis sensor->sensor.value = value;
8841e8e22a4Skettenis sensor->sensor.flags = 0;
885309d5690Smglocker }
886309d5690Smglocker return (value);
8871e8e22a4Skettenis }
8881e8e22a4Skettenis
8891e8e22a4Skettenis void
smu_refresh_sensors(void * arg)8901e8e22a4Skettenis smu_refresh_sensors(void *arg)
8911e8e22a4Skettenis {
8921e8e22a4Skettenis struct smu_softc *sc = arg;
8931e8e22a4Skettenis int i;
8941e8e22a4Skettenis
895769768baSthib rw_enter_write(&sc->sc_lock);
8961e8e22a4Skettenis for (i = 0; i < sc->sc_num_sensors; i++)
897309d5690Smglocker smu_sensor_refresh(sc, &sc->sc_sensors[i], 1);
8981e8e22a4Skettenis for (i = 0; i < sc->sc_num_fans; i++)
8991e8e22a4Skettenis smu_fan_refresh(sc, &sc->sc_fans[i]);
900769768baSthib rw_exit_write(&sc->sc_lock);
90126c1ffa3Skettenis }
9022e975dd8Skettenis
903309d5690Smglocker /*
904309d5690Smglocker * Wrapper functions for the thermal management framework.
905309d5690Smglocker */
906309d5690Smglocker int
smu_fan_set_rpm_thermal(struct smu_fan * fan,int rpm)907309d5690Smglocker smu_fan_set_rpm_thermal(struct smu_fan *fan, int rpm)
908309d5690Smglocker {
909309d5690Smglocker struct smu_softc *sc = smu_cd.cd_devs[0];
910309d5690Smglocker
911309d5690Smglocker rw_enter_write(&sc->sc_lock);
912309d5690Smglocker (void)smu_fan_set_rpm(sc, fan, rpm);
913309d5690Smglocker rw_exit_write(&sc->sc_lock);
914309d5690Smglocker
915309d5690Smglocker return (0);
916309d5690Smglocker }
917309d5690Smglocker
918309d5690Smglocker int
smu_fan_set_pwm_thermal(struct smu_fan * fan,int pwm)919309d5690Smglocker smu_fan_set_pwm_thermal(struct smu_fan *fan, int pwm)
920309d5690Smglocker {
921309d5690Smglocker struct smu_softc *sc = smu_cd.cd_devs[0];
922309d5690Smglocker
923309d5690Smglocker rw_enter_write(&sc->sc_lock);
924309d5690Smglocker (void)smu_fan_set_pwm(sc, fan, pwm);
925309d5690Smglocker rw_exit_write(&sc->sc_lock);
926309d5690Smglocker
927309d5690Smglocker return (0);
928309d5690Smglocker }
929309d5690Smglocker
930309d5690Smglocker int
smu_sensor_refresh_thermal(struct smu_sensor * sensor)931309d5690Smglocker smu_sensor_refresh_thermal(struct smu_sensor *sensor)
932309d5690Smglocker {
933309d5690Smglocker struct smu_softc *sc = smu_cd.cd_devs[0];
934309d5690Smglocker int value;
935309d5690Smglocker
936309d5690Smglocker rw_enter_write(&sc->sc_lock);
937309d5690Smglocker value = smu_sensor_refresh(sc, sensor, 0);
938309d5690Smglocker rw_exit_write(&sc->sc_lock);
939309d5690Smglocker
940309d5690Smglocker return (value);
941309d5690Smglocker }
942309d5690Smglocker
9432e975dd8Skettenis int
smu_i2c_acquire_bus(void * cookie,int flags)9442e975dd8Skettenis smu_i2c_acquire_bus(void *cookie, int flags)
9452e975dd8Skettenis {
9462e975dd8Skettenis struct smu_softc *sc = cookie;
9472e975dd8Skettenis
9482e975dd8Skettenis if (flags & I2C_F_POLL)
9492e975dd8Skettenis return (0);
9502e975dd8Skettenis
951769768baSthib return (rw_enter(&sc->sc_lock, RW_WRITE));
9522e975dd8Skettenis }
9532e975dd8Skettenis
9542e975dd8Skettenis void
smu_i2c_release_bus(void * cookie,int flags)9552e975dd8Skettenis smu_i2c_release_bus(void *cookie, int flags)
9562e975dd8Skettenis {
9572e975dd8Skettenis struct smu_softc *sc = cookie;
9582e975dd8Skettenis
9592e975dd8Skettenis if (flags & I2C_F_POLL)
9602e975dd8Skettenis return;
9612e975dd8Skettenis
962769768baSthib rw_exit(&sc->sc_lock);
9632e975dd8Skettenis }
9642e975dd8Skettenis
9652e975dd8Skettenis int
smu_i2c_exec(void * cookie,i2c_op_t op,i2c_addr_t addr,const void * cmdbuf,size_t cmdlen,void * buf,size_t len,int flags)9662e975dd8Skettenis smu_i2c_exec(void *cookie, i2c_op_t op, i2c_addr_t addr,
9672e975dd8Skettenis const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags)
9682e975dd8Skettenis {
9692e975dd8Skettenis struct smu_softc *sc = cookie;
9702e975dd8Skettenis struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd;
9712e975dd8Skettenis u_int8_t smu_op = SMU_I2C_NORMAL;
9722e975dd8Skettenis int error, retries = 10;
9732e975dd8Skettenis
9742e975dd8Skettenis if (!I2C_OP_STOP_P(op) || cmdlen > 3 || len > 5)
9752e975dd8Skettenis return (EINVAL);
9762e975dd8Skettenis
9772e975dd8Skettenis if(cmdlen == 0)
9782e975dd8Skettenis smu_op = SMU_I2C_SIMPLE;
9792e975dd8Skettenis else if (I2C_OP_READ_P(op))
9802e975dd8Skettenis smu_op = SMU_I2C_COMBINED;
9812e975dd8Skettenis
9822e975dd8Skettenis cmd->cmd = SMU_I2C;
9832e975dd8Skettenis cmd->len = 9 + len;
9842e975dd8Skettenis cmd->data[0] = 0xb;
9852e975dd8Skettenis cmd->data[1] = smu_op;
9862e975dd8Skettenis cmd->data[2] = addr << 1;
9872e975dd8Skettenis cmd->data[3] = cmdlen;
9882e975dd8Skettenis memcpy (&cmd->data[4], cmdbuf, cmdlen);
9892e975dd8Skettenis cmd->data[7] = addr << 1 | I2C_OP_READ_P(op);
9902e975dd8Skettenis cmd->data[8] = len;
9912e975dd8Skettenis memcpy(&cmd->data[9], buf, len);
9922e975dd8Skettenis
9932e975dd8Skettenis error = smu_do_cmd(sc, 250);
9942e975dd8Skettenis if (error)
9952e975dd8Skettenis return error;
9962e975dd8Skettenis
9972e975dd8Skettenis while (retries--) {
9982e975dd8Skettenis cmd->cmd = SMU_I2C;
9992e975dd8Skettenis cmd->len = 1;
10002e975dd8Skettenis cmd->data[0] = 0;
10012e975dd8Skettenis memset(&cmd->data[1], 0xff, len);
10022e975dd8Skettenis
10032e975dd8Skettenis error = smu_do_cmd(sc, 250);
10042e975dd8Skettenis if (error)
10052e975dd8Skettenis return error;
10062e975dd8Skettenis
10072e975dd8Skettenis if ((cmd->data[0] & 0x80) == 0)
10082e975dd8Skettenis break;
10092e975dd8Skettenis if (cmd->data[0] == 0xfd)
10102e975dd8Skettenis break;
10112e975dd8Skettenis
10122e975dd8Skettenis DELAY(15 * 1000);
10132e975dd8Skettenis }
10142e975dd8Skettenis
10152e975dd8Skettenis if (cmd->data[0] & 0x80)
10162e975dd8Skettenis return (EIO);
10172e975dd8Skettenis
10182e975dd8Skettenis if (I2C_OP_READ_P(op))
10192e975dd8Skettenis memcpy(buf, &cmd->data[1], len);
10202e975dd8Skettenis return (0);
10212e975dd8Skettenis }
1022b6032f37Skettenis
1023b6032f37Skettenis void
smu_slew_voltage(u_int freq_scale)1024b6032f37Skettenis smu_slew_voltage(u_int freq_scale)
1025b6032f37Skettenis {
1026b6032f37Skettenis struct smu_softc *sc = smu_cd.cd_devs[0];
1027b6032f37Skettenis struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd;
1028b6032f37Skettenis
1029be3e4ce7Skettenis rw_enter_write(&sc->sc_lock);
1030be3e4ce7Skettenis
1031b6032f37Skettenis cmd->cmd = SMU_POWER;
1032b6032f37Skettenis cmd->len = 8;
1033b6032f37Skettenis memcpy(cmd->data, "VSLEW", 5);
1034b6032f37Skettenis cmd->data[5] = 0xff;
1035b6032f37Skettenis cmd->data[6] = 1;
1036b6032f37Skettenis cmd->data[7] = freq_scale;
1037b6032f37Skettenis
1038b6032f37Skettenis smu_do_cmd(sc, 250);
1039be3e4ce7Skettenis
1040be3e4ce7Skettenis rw_exit_write(&sc->sc_lock);
1041b6032f37Skettenis }
1042